From 03434dca109f6b5141013fa94d4d1f662e208a9d Mon Sep 17 00:00:00 2001 From: Kacper Kula Date: Wed, 30 Apr 2025 12:38:53 +0100 Subject: [PATCH] feat: linking to other files from codeblock --- CHANGELOG.md | 4 +++ manifest.json | 2 +- package.json | 2 +- src/editorExtension/syntaxHighlight.ts | 27 ++++++++++++++++--- src/editorExtension/widgets/FilePathWidget.ts | 17 ++++++++++++ src/grammar/highlighterOperation.ts | 2 +- src/grammar/parser.test.ts | 12 +++++++++ src/grammar/parser.ts | 20 ++++++++++++-- versions.json | 3 ++- 9 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 src/editorExtension/widgets/FilePathWidget.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8268b58..4ca52bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.34.0 (2025-04-30) +- feat: you can now navigate to linked CSV and JSON file sources +- feat: improved support for minimal theme custom classes + # 0.33.0 (2025-04-09) - fix: better text links for images (by @satkowski) - fix: lists are now rendered properly (by @satkowski) diff --git a/manifest.json b/manifest.json index 2b4638e..31bb508 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "sqlseal", "name": "SQLSeal", - "version": "0.33.0", + "version": "0.34.0", "minAppVersion": "0.15.0", "description": "Use SQL in your notes to query your vault files and CSV content.", "author": "hypersphere", diff --git a/package.json b/package.json index 1710693..356d7c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sqlseal", - "version": "0.33.0", + "version": "0.34.0", "description": "A plugin for Obsidian that allows you to run SQL queries on your notes.", "main": "main.js", "scripts": { diff --git a/src/editorExtension/syntaxHighlight.ts b/src/editorExtension/syntaxHighlight.ts index 1a56b7c..1da56e8 100644 --- a/src/editorExtension/syntaxHighlight.ts +++ b/src/editorExtension/syntaxHighlight.ts @@ -15,6 +15,7 @@ import { SQLSealLangDefinition } from '../grammar/parser'; import { RendererRegistry } from '../renderer/rendererRegistry'; import { Range } from '@codemirror/state'; import { Decorator, highlighterOperation } from '../grammar/highlighterOperation'; +import { FilePathWidget } from './widgets/FilePathWidget'; const markDecorations = { blockFlag: Decoration.mark({ class: 'cm-sqlseal-block-flag' }), @@ -85,12 +86,32 @@ export class SQLSealViewPlugin implements PluginValue { if (decorations) { decorations.forEach(dec => { - const decoration = markDecorations[dec.type as keyof typeof markDecorations]; - if (decoration) { - builder.push(decoration.range( + if (dec.type === 'filename') { + // Get the actual filename text from the document + const filePath = view.state.doc.sliceString( + contentStart + dec.start, + contentStart + dec.end + ); + + console.log('FOUND FILENAME', filePath) + + // Create widget decoration for the filename + const widget = new FilePathWidget(filePath, this.app); + builder.push(Decoration.replace({ + widget, + inclusive: true + }).range( contentStart + dec.start, contentStart + dec.end )); + } else { + const decoration = markDecorations[dec.type as keyof typeof markDecorations]; + if (decoration) { + builder.push(decoration.range( + contentStart + dec.start, + contentStart + dec.end + )); + } } }); } diff --git a/src/editorExtension/widgets/FilePathWidget.ts b/src/editorExtension/widgets/FilePathWidget.ts new file mode 100644 index 0000000..97f3add --- /dev/null +++ b/src/editorExtension/widgets/FilePathWidget.ts @@ -0,0 +1,17 @@ +import { WidgetType } from "@codemirror/view"; +import { App, Notice } from "obsidian"; + +export class FilePathWidget extends WidgetType { + constructor(private filePath: string, private app: App) { + super(); + } + + toDOM() { + const link = document.createElement("a"); + link.textContent = this.filePath; + link.href = this.filePath + link.className = "internal-link"; + + return link; + } +} \ No newline at end of file diff --git a/src/grammar/highlighterOperation.ts b/src/grammar/highlighterOperation.ts index 68722bb..da22a1c 100644 --- a/src/grammar/highlighterOperation.ts +++ b/src/grammar/highlighterOperation.ts @@ -11,7 +11,7 @@ const nodes = new Map([ ['tableKeyword', { terminal: true, type: 'keyword' }], ['fileOpening', { terminal: true, type: 'function' }], ['tableOpening', { terminal: true, type: 'function' }], - ['filename', { terminal: true, type: 'identifier' }], + ['filename', { terminal: true, type: 'filename' }], ['digit', { terminal: true, type: 'identifier' }], ['tableDefinitionClosing', { terminal: true, type: 'function' }], ['selectKeyword', { terminal: false, type: 'keyword' }], diff --git a/src/grammar/parser.test.ts b/src/grammar/parser.test.ts index 956d85c..3dc0cb1 100644 --- a/src/grammar/parser.test.ts +++ b/src/grammar/parser.test.ts @@ -37,6 +37,18 @@ describe('Ohm parser', () => { }) }) + it('should parse table expression with name quoted', () => { + expect(parse('TABLE x = file("abcdef.csv")', DEFAULT_VIEWS)).toEqual({ + query: '', + tables: [{ arguments: ['abcdef.csv'], type: 'file', tableAlias: 'x' }], + flags: {}, + renderer: { + name: 'GRID', + options: '' + } + }) + }) + it('should parse SELECT statement alone', () => { expect(parse('SELECT * FROM files', DEFAULT_VIEWS)).toEqual({ query: 'SELECT * FROM files', diff --git a/src/grammar/parser.ts b/src/grammar/parser.ts index 478137a..e6e409e 100644 --- a/src/grammar/parser.ts +++ b/src/grammar/parser.ts @@ -34,8 +34,9 @@ export const SQLSealLangDefinition = (views: ViewDefinition[], flags: readonly F | caseInsensitive<"EXPLAIN"> -- explain ${flags.length ? '| ExtraFlags -- extraFlags' : ''} TableExpression = tableKeyword identifier "=" TableDefinition - TableDefinition = fileOpening NonemptyListOf tableDefinitionClosing -- file + TableDefinition = fileOpening TableFileExpressionArgs tableDefinitionClosing -- file | tableOpening NonemptyListOf tableDefinitionClosing -- mdtable + TableFileExpressionArgs = filename ("," NonemptyListOf)? identifier = (alnum | "_")+ filename = (alnum | "." | "-" | space | "_" | "/" | "\\" | "$" | "[" | "]" | "\"")+ fileOpening = caseInsensitive<"file("> @@ -109,10 +110,18 @@ const generateSemantic = (grammar: ohm.Grammar) => { }, TableDefinition_file: (_file, args, _close) => { return { - arguments: args.asIteration().children.map((c: ohm.Node) => c.toObject().trim()), + arguments: args.toObject(), type: 'file' } }, + TableFileExpressionArgs: (filename, _sep, rest) => { + // ...rest.asIteration().children.map((c: ohm.Node) => c.toObject().trim()) + let remaining = [] + if (rest.children.length) { + remaining = rest.children[0].asIteration().children.map((c: ohm.Node) => c.toObject().trim()) + } + return [filename.toObject(), ...remaining] + }, TableDefinition_mdtable: (_file, args, _close) => { return { arguments: args.asIteration().children.map((c: ohm.Node) => c.toObject().trim()), @@ -136,6 +145,13 @@ const generateSemantic = (grammar: ohm.Grammar) => { }, listElement_quoted: (_q, value, _q2) => value.sourceString, listElement_unquoted: (v) => v.sourceString, + filename: (v) => { + const f = v.sourceString + if (f.length && f[0] === '"' && f[f.length - 1] === '"') { + return f.substring(1, f.length - 1) + } + return v.sourceString.trim() + }, _terminal() { return this.sourceString } diff --git a/versions.json b/versions.json index 25e3cf8..4c991a4 100644 --- a/versions.json +++ b/versions.json @@ -59,5 +59,6 @@ "0.30.1": "0.15.0", "0.31.0": "0.15.0", "0.32.0": "0.15.0", - "0.33.0": "0.15.0" + "0.33.0": "0.15.0", + "0.34.0": "0.15.0" } \ No newline at end of file