diff --git a/examples/Untitled.ipynb b/examples/Untitled.ipynb index 65a5f99..33f8810 100644 --- a/examples/Untitled.ipynb +++ b/examples/Untitled.ipynb @@ -2,709 +2,244 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, - "id": "02ab81b2-33b5-41b7-8cf3-fc4d72bbf4f6", + "execution_count": 15, + "id": "94b4116d-7d70-4c75-b5e9-62eb3b20c5ad", "metadata": {}, "outputs": [], "source": [ - "import dyno" + "from dyno.dynofile import LDynoModel as DynoModel" ] }, { "cell_type": "code", - "execution_count": 35, - "id": "c00db4e9-d9a6-43ce-ba65-6b20f865e7f2", + "execution_count": 16, + "id": "8b705a25-2cff-4c95-b2d7-a3383d2c4d1f", "metadata": {}, "outputs": [], "source": [ - "from dyno.report import dsge_report" + "model = DynoModel(\"ICAI/partial.dyno\")" ] }, { "cell_type": "code", - "execution_count": 41, - "id": "d57416c1-c112-4cda-8487-215a07db716f", - "metadata": {}, - "outputs": [], - "source": [ - "report = dsge_report(\"example1.mod\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f2651998-731a-4f4f-9021-d7ef28a0869c", - "metadata": {}, - "outputs": [], - "source": [ - "from dyno.dynofile import DynoModel" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "68fa525e-d110-458a-8f12-40e8967741cd", - "metadata": {}, - "outputs": [], - "source": [ - "model = DynoModel(\"example1.dyno\")" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "id": "c6053ac1-004a-40ea-83d4-8afe6632131a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

Model

\n", - "\n" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "model" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "id": "d8ee8a4b-4c0d-4c4a-bf46-bd4501783379", - "metadata": {}, - "outputs": [], - "source": [ - "dr = model.solve()" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "id": "6ca5a00f-4708-478f-91a2-fc1f5e0dd0d4", - "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "'RecursiveSolution' object has no attribute 'plot'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[52]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mdr\u001b[49m\u001b[43m.\u001b[49m\u001b[43mplot\u001b[49m\n", - "\u001b[31mAttributeError\u001b[39m: 'RecursiveSolution' object has no attribute 'plot'" - ] - } - ], - "source": [ - "dr.plot" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "251c07e6-8e0e-4ee0-a488-67dc62ed8b27", - "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "cannot import name 'deterministic_solve' from 'dyno.solver' (/home/pablo/Econforge/dyno.py/src/dyno/solver.py)", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mImportError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mdyno\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01msolver\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m deterministic_solve\n", - "\u001b[31mImportError\u001b[39m: cannot import name 'deterministic_solve' from 'dyno.solver' (/home/pablo/Econforge/dyno.py/src/dyno/solver.py)" - ] - } - ], - "source": [ - "from dyno.solver import deterministic_solve" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa620b07-ee4f-4152-a82e-491bc59af7b5", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import *" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3c96291e-e35f-410e-b9e2-d55d19a8a3a1", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets.widgets import *" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "092d0108-1156-4369-b463-0f8b19f84c38", - "metadata": {}, - "outputs": [], - "source": [ - "sim = deterministic_solve(model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e0b8224-f314-4c73-8261-08bb7cd98df8", - "metadata": {}, - "outputs": [], - "source": [ - "tab_nest = widgets.Accordion([\n", - " widgets.HTML(model._repr_html_()),\n", - " widgets.HTML(sim._repr_html_())\n", - "])\n", - "tab_nest.titles = ('An accordion', 'Simulation')\n", - "tab_nest" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb29370d-0c2f-46c1-9769-36d5e8ef6b51", - "metadata": {}, - "outputs": [], - "source": [ - "import xarray" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64f6c01c-397f-4947-8efa-694d5e78f2b9", - "metadata": {}, - "outputs": [], - "source": [ - "sim.to_xarray()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73fdb09d-adc2-4b94-b73f-3ac2aed6381d", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d83f36a-be38-4918-8caa-9061c0f18468", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets.embed import embed_minimal_html\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e89ef6cd-171d-480e-a6ae-2e221f825fe7", - "metadata": {}, - "outputs": [], - "source": [ - "embed_minimal_html(\"out.html\" , [tab_nest])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eab1207c-27f6-4a28-9738-ff75dd70a3a0", - "metadata": {}, - "outputs": [], - "source": [ - "import altair as alt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e396c6c-496a-4d9f-b76e-4d4c5f301431", - "metadata": {}, - "outputs": [], - "source": [ - "sim['t'] = sim.index" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "06de5e83-4f40-4328-843f-cc81fd1306eb", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas\n", - "ssim = pandas.melt(sim, id_vars=['t'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1e59c8d-1913-4d95-9e35-89ced11640e8", - "metadata": {}, - "outputs": [], - "source": [ - "alt.Chart(ssim).mark_line().encode(\n", - " x = 't',\n", - " y = 'value',\n", - " color=alt.Y(alt.repeat(\"row\"), type='quantitative'),\n", - ").properties(\n", - " width=200,\n", - " height=200\n", - ").repeat(\n", - " row=ssim['variable'].unique()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b56d4b0c-f923-4047-a7b1-67e65973e673", - "metadata": {}, - "outputs": [], - "source": [ - "ssim" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "924e01c7-91c6-4eb0-9fd2-87623aea5815", - "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "name 'alt' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43malt\u001b[49m.Chart(ssim).mark_line().encode(\n\u001b[32m 2\u001b[39m x=\u001b[33m'\u001b[39m\u001b[33mt\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 3\u001b[39m y=\u001b[33m'\u001b[39m\u001b[33mvalue\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 4\u001b[39m color=\u001b[33m'\u001b[39m\u001b[33mvariable\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 5\u001b[39m ).properties(\n\u001b[32m 6\u001b[39m width=\u001b[32m180\u001b[39m,\n\u001b[32m 7\u001b[39m height=\u001b[32m180\u001b[39m\n\u001b[32m 8\u001b[39m ).facet(\n\u001b[32m 9\u001b[39m row=\u001b[33m'\u001b[39m\u001b[33mvariable\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 10\u001b[39m )\n", - "\u001b[31mNameError\u001b[39m: name 'alt' is not defined" - ] - } - ], - "source": [ - "alt.Chart(ssim).mark_line().encode(\n", - " x='t',\n", - " y='value',\n", - " color='variable'\n", - ").properties(\n", - " width=180,\n", - " height=180\n", - ").facet(\n", - " row='variable'\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "6218c7b1-d379-43c8-b583-67e14baf4497", - "metadata": {}, - "outputs": [], - "source": [ - "import myst_parser" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "e97e484d-73b3-4bd6-a83f-a6e0170b38da", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[31mSignature:\u001b[39m myst_parser.setup(app)\n", - "\u001b[31mDocstring:\u001b[39m Initialize the [Sphinx](https://github.com/sphinx-doc/sphinx) extension.\n", - "\u001b[31mFile:\u001b[39m ~/Econforge/jupyterlab-dyno/.pixi/envs/default/lib/python3.13/site-packages/myst_parser/__init__.py\n", - "\u001b[31mType:\u001b[39m function" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "myst_parser.setup?" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "124e32e8-cb20-4472-b73f-7b760bcd0bcc", - "metadata": {}, - "outputs": [], - "source": [ - "import myst_parser.docutils_" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "12e8fda1-68e2-4bb8-98be-c8bfc2dd9333", - "metadata": {}, - "outputs": [], - "source": [ - "import jupyterlab_myst" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "id": "f2e93ede-56e2-48c5-aac8-e1c58d14d578", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "doc = \"\"\"\n", - "\n", - "# Here is a title.\n", - "\n", - "Here's some *text*\n", - "\n", - "1. a list\n", - "\n", - "> a quote\n", - "\n", - "{emphasis}`content`\n", - "\n", - "Can I embed formulas : $a=34$ ?\n", - "```{sidebar} my sidebar\n", - "content\n", - "```\n", - ":::{tip}\n", - "Let's give readers a helpful hint!\n", - ":::\n", - "\n", - "\"\"\"\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 87, - "id": "d39c739a-7fc4-41ff-8611-8f469d90e0ac", - "metadata": {}, - "outputs": [], - "source": [ - "from docutils.core import publish_string\n", - "from myst_parser.docutils_ import Parser\n", - "\n", - "output = publish_string(\n", - " source=doc,\n", - " writer_name=\"html5\",\n", - " settings_overrides={\n", - " # \"myst_enable_extensions\": [\"deflist\"],\n", - " \"embed_stylesheet\": True,\n", - " },\n", - " parser=Parser(),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 88, - "id": "9c5564a7-7b35-4f0d-8e0e-2afb3db7b85b", - "metadata": {}, - "outputs": [], - "source": [ - "html = HTML(output.decode(\"utf-8\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 89, - "id": "91377fe7-1331-4b5f-a132-0a5b9f238cb5", + "execution_count": 17, + "id": "b08fd73e-74ae-43c9-9416-207feb4e6681", "metadata": {}, "outputs": [ { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "dea9871a261e47ba96a5ca4c6173cab9", - "version_major": 2, - "version_minor": 0 - }, "text/plain": [ - "HTML(value='\\n\\n\\n", - "evalue": "write() argument must be str, not bytes", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[63]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mopen\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mout.html\u001b[39m\u001b[33m\"\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mf\u001b[49m\u001b[43m.\u001b[49m\u001b[43mwrite\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[31mTypeError\u001b[39m: write() argument must be str, not bytes" - ] - } - ], - "source": [ - "with open(\"out.html\") as f:\n", - " f.write(output)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f7956b2-bd69-4761-a4c7-921b820c7024", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ff310d1-72af-4762-9589-44d001371ca0", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 94, - "id": "41c788bc-9939-4720-8277-361fa1dc8940", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import Accordion, HTML\n", - "\n", - "acc = Accordion(children=[\n", - " HTML(\"JI\"),\n", - " HTML(\"JI\")\n", - "])\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 95, - "id": "418c5cc5-9b69-4fdd-8a7a-747f5288761e", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets.embed import embed_minimal_html\n", - "\n", - "embed_minimal_html('export.html', views=[acc], title='Widgets export')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5cd45813-a18b-461f-9a76-9cc9b47096ff", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4fe195ea-c13c-4fa2-a563-fc48b861f9a5", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "0b4742fe-8fbf-4a62-8fb0-8925f16347c6", - "metadata": {}, - "outputs": [], - "source": [ - "class HTML:\n", - " def __init__(self, data):\n", - " self.data = data\n", - "\n", - " def _repr_html_(self):\n", - " return self.data\n", - "\n", - "doc = \"\"\"\n", - "

\n", - "This is basic html.\n", - "

\n", - "\"\"\"\n", - "\n", - "md = \"\"\"\n", - "# This is basic markdown\n", - "\n", - "\n", - "But there aren't many nice options.\n", - "\n", - "$a = 4$.\n", - "\n", - "\n", - "\"\"\"\n", - "\n", - "import mistune\n", - "\n", - "html = HTML(mistune.html(md))\n", - "\n", - "\n", - "from markdown_it import MarkdownIt\n", - "from mdit_py_plugins.front_matter import front_matter_plugin\n", - "from mdit_py_plugins.footnote import footnote_plugin\n", - "\n", - "md = \"\"\"\n", - "---\n", - "title: 1\n", - "---\n", - "\n", - "# Is that meant to be a title ?\n", - "\n", - "a | b\n", - "- | -\n", - "1 | 2\n", - "\n", - "```\n", - "Sample text here...\n", - "```\n", - "::: {.note}\n", - "\n", - "Can I make a note here ?\n", - "\n", - ":::\n", - "\n", - "A footnote [^1]\n", - "\n", - "[^1]: some details\n", - "\"\"\"\n", - "\n", - "#html = HTML(html_text)\n", - "\n", - "\n", - "from IPython.display import display, Markdown, Latex\n", - "\n", - "\n", - "html = Markdown(md)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "e93cb822-a1f4-4376-89de-3334fe23917c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/markdown": [ - "\n", - "---\n", - "title: 1\n", - "---\n", - "\n", - "# Is that meant to be a title ?\n", - "\n", - "a | b\n", - "- | -\n", - "1 | 2\n", - "\n", - "```\n", - "Sample text here...\n", - "```\n", - "::: {.note}\n", - "\n", - "Can I make a note here ?\n", - "\n", - ":::\n", - "\n", - "A footnote [^1]\n", - "\n", - "[^1]: some details\n" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "html" - ] - }, - { - "cell_type": "markdown", - "id": "580482c9-9de1-4714-9883-a5a24ed24bae", - "metadata": {}, - "source": [ - "---\n", - "title: 1\n", - "---\n", - "\n", - "# Is that meant to be a title ?\n", - "\n", - "a | b\n", - "- | -\n", - "1 | 2\n", - "\n", - "```\n", - "Sample text here...\n", - "```\n", - "::: {.note}\n", - "\n", - "Can I make a note here ?\n", - "\n", - ":::\n", - "\n", - "A footnote [^1]\n", - "\n", - "[^1]: some details" + "model.jacobians" ] }, { "cell_type": "code", "execution_count": null, - "id": "b67ccab4-f7c3-4cb4-a880-ffed590a1658", + "id": "90823573-bc17-4155-8a6c-449f9fe00e31", "metadata": {}, "outputs": [], "source": [] @@ -712,7 +247,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python . (XPython)", + "display_name": "Python 3.13 (XPython)", "language": "python", "name": "xpython" }, diff --git a/examples/Untitled1.ipynb b/examples/Untitled1.ipynb deleted file mode 100644 index 567e6da..0000000 --- a/examples/Untitled1.ipynb +++ /dev/null @@ -1,170 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 3, - "id": "b00db412-890e-46a3-b73d-d70569486010", - "metadata": {}, - "outputs": [], - "source": [ - "txt = \"\"\"\n", - "# This is markdown, brother.\n", - "\n", - "This is some text.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "9b1c8b73-a69e-4b50-9f3e-533b4d440e8d", - "metadata": {}, - "outputs": [], - "source": [ - "class Report:\n", - " \n", - " def __init__(self, txt):\n", - " self.txt = txt\n", - " def _repr_markdown_(self):\n", - " return f\"{self.txt}\"\n", - " def _repr_html_(self):\n", - " return f\"
{self.txt}
\"" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "eb985f07-5b9e-4e07-aa19-3f47e394fdaa", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n",
-       "# This is markdown, brother.\n",
-       "\n",
-       "This is some text.\n",
-       "
" - ], - "text/markdown": [ - "\n", - "# This is markdown, brother.\n", - "\n", - "This is some text.\n" - ], - "text/plain": [ - "<__main__.Report at 0x7ba12bccfcb0>" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Report(txt)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "c2d213a7-cbd7-47b0-80f2-68be72db0a62", - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import Markdown" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "44d67b29-2338-4c1a-9ea8-a5a7da637636", - "metadata": {}, - "outputs": [], - "source": [ - "m = Markdown(txt)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "15cd5210-b7b5-477e-8eb7-017642245740", - "metadata": {}, - "outputs": [ - { - "data": { - "text/markdown": [ - "\n", - "# This is markdown, brother.\n", - "\n", - "This is some text.\n" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "d383b6cc-dafd-41bb-bf42-13d56fc2315e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'\\n# This is markdown, brother.\\n\\nThis is some text.\\n'" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m._repr_markdown_()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d744242b-799d-4a88-ae0c-70a5d7f19b42", - "metadata": {}, - "outputs": [], - "source": [ - "m._" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0705c31e-5aee-4c6e-905b-b4f13494aee6", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python . (XPython)", - "language": "python", - "name": "xpython" - }, - "language_info": { - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "version": "3.13.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/demo.dyno b/examples/demo.dyno index 3bd56ea..5ec2d4d 100644 --- a/examples/demo.dyno +++ b/examples/demo.dyno @@ -2,9 +2,9 @@ beta <- 0.98 delta <- 0.025 nss <- 0.33 -eta <- 1.3 +eta <- 1. rho <- 0.5 -alpha <- .33 +alpha <- .37 khi <- (1-alpha)*(1-nss)^eta/nss*(1/beta-1+delta)/(1/beta-1+delta-delta*alpha) # equations diff --git a/examples/example1.dyno b/examples/example1.dyno index 6151636..64cb42d 100644 --- a/examples/example1.dyno +++ b/examples/example1.dyno @@ -4,7 +4,7 @@ delta <- 0.025 nss <- 0.33 eta <- 1.0 alpha <- .32 -rho <- .96 +rho <- .9 chi <- (1-alpha)*(1-nss)^eta/nss*(1/beta-1+delta)/(1/beta-1+delta-delta*alpha) # equations diff --git a/examples/example1.mod b/examples/example1.mod index bc89805..85e90e3 100644 --- a/examples/example1.mod +++ b/examples/example1.mod @@ -35,7 +35,6 @@ beta = 0.99; delta = 0.02509; psi = 0; theta = 2.95; - phi = 0.1; model; diff --git a/package.json b/package.json index cf05365..e683430 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,12 @@ "watch:labextension": "jupyter labextension watch ." }, "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.2.0", "@jupyterlab/application": "^4.4.5", - "@jupyterlab/settingregistry": "^4.4.5" + "@jupyterlab/codemirror": "^4.4.5", + "@jupyterlab/settingregistry": "^4.4.5", + "@lezer/highlight": "^1.0.0" }, "devDependencies": { "@jupyterlab/builder": "^4.4.5", diff --git a/pixi.toml b/pixi.toml index 06e5c97..186ff40 100644 --- a/pixi.toml +++ b/pixi.toml @@ -10,11 +10,14 @@ watch = "jlpm watch & jupyter lab" [dependencies] jupyterlab = ">=4.0.0,<5" jupyterlab_dyno = { path = "." } -dyno = ">=0.1.5.dev7" nodejs = ">=24.4.0,<24.5" jupyterlab-myst = ">=2.4.2,<3" tempita = ">=0.6.0,<0.7" vega_datasets = ">=0.9.0,<0.10" +interegular = ">=0.3.3,<0.4" +altair = ">=5.5.0,<6" +rich = ">=14.1.0,<15" +comm = ">=0.2.3,<0.3" [package] name = "jupyterlab_dyno" @@ -30,9 +33,10 @@ hatch-jupyter-builder = ">=0.5.0" nodejs = ">=24.4.0,<24.5" jupyterlab = ">=4.0.0,<5" + [package.run-dependencies] +dyno = { path = "/home/pablo/Econforge/dyno.py"} #dyno = ">=0.1.5.dev7" -#dyno = { path = "/home/pablo/Econforge/dyno.py" } -dyno = ">=0.1.6" +#dyno = ">=0.1.6" xeus-python = ">=0.17.4,<0.18" nlohmann_json = "==3.12" diff --git a/src/index.ts b/src/index.ts index ccc55d4..8d34cf1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,8 @@ import { IWidgetTracker, WidgetTracker } from '@jupyterlab/apputils'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; +import { IEditorLanguageRegistry } from '@jupyterlab/codemirror'; + import { Token } from '@lumino/coreutils'; import { SessionContext } from '@jupyterlab/apputils'; @@ -32,6 +34,8 @@ import { ILayoutRestorer } from '@jupyterlab/application'; +import { dyno, mod } from './languages/dyno-language'; + // Default settings, see schema/plugin.json for more details let global_setting = {}; let preserveScrollPosition = true; @@ -203,6 +207,7 @@ export class DynareWidget const start = performance.now(); console.log(global_setting); + // Choose kernel code based on file type const code = `import warnings import json @@ -222,6 +227,8 @@ dsge_report(txt=txt, filename=filename, **options)`; // const prevOutputs = this._safeToJSON(this.content.model); + // Register comm handler after we're sure the kernel is ready (just before execution) + this._setupHighlightingComm(); OutputArea.execute(code, tempArea, this._sessionContext) .then((msg: KernelMessage.IExecuteReplyMsg | undefined) => { @@ -237,6 +244,11 @@ dsge_report(txt=txt, filename=filename, **options)`; // } if (nextOutputs) { this._applyOutputsToVisibleModel(nextOutputs); + + // Note: highlighting is now handled via comm, not from outputs + // Keep the fallback for backward compatibility + console.log('[DEBUG] About to call _performHighlightingBasedOnResults with outputs:', nextOutputs); + this._performHighlightingBasedOnResults(nextOutputs); } // Restore scroll position after DOM/content updates @@ -293,6 +305,170 @@ dsge_report(txt=txt, filename=filename, **options)`; }); } + /** + * Set up Jupyter Comm for receiving highlighting data from the kernel + */ + private _setupHighlightingComm(): void { + try { + if (!this._sessionContext?.session?.kernel) { + console.warn('[DEBUG] No kernel available for comm setup'); + return; + } + + const kernel = this._sessionContext.session.kernel; + const commTargetName = 'jupyterlab-dyno-highlighting'; + + console.log('[DEBUG] Setting up comm with kernel:', kernel.id); + console.log('[DEBUG] Kernel status:', kernel.status); + + // Check if kernel is ready + if (kernel.status !== 'idle' && kernel.status !== 'busy') { + console.warn('[DEBUG] Kernel not ready for comm setup, status:', kernel.status); + // Retry after a delay + setTimeout(() => { + console.log('[DEBUG] Retrying comm setup...'); + this._setupHighlightingComm(); + }, 1000); + return; + } + + // Clean up any existing comm targets first + try { + // Note: removeCommTarget may not exist or may have different signature + // This is just for cleanup, so we can ignore errors + if (typeof (kernel as any).removeCommTarget === 'function') { + (kernel as any).removeCommTarget(commTargetName, () => {}); + console.log('[DEBUG] Cleaned up existing comm target'); + } + } catch (e) { + console.log('[DEBUG] Comm cleanup ignored (expected):', e); + } + + // Register the comm target with defensive error handling + try { + kernel.registerCommTarget(commTargetName, (comm, msg) => { + try { + console.log('[DEBUG] ===== COMM MESSAGE RECEIVED ====='); + console.log('[DEBUG] Comm object:', comm); + console.log('[DEBUG] Message:', msg); + console.log('[DEBUG] Message content:', msg.content); + console.log('[DEBUG] Message data:', msg.content.data); + console.log('[DEBUG] ==================================='); + + this._handleHighlightingData(msg.content.data); + + // Set up message handler for future messages on this comm + comm.onMsg = (msg) => { + try { + console.log('[DEBUG] ===== SUBSEQUENT COMM MESSAGE ====='); + console.log('[DEBUG] Received comm message:', msg.content.data); + console.log('[DEBUG] ===================================='); + this._handleHighlightingData(msg.content.data); + } catch (handlerError) { + console.error('[DEBUG] Error in comm message handler:', handlerError); + } + }; + + // Handle comm close + comm.onClose = (msg) => { + try { + console.log('[DEBUG] Comm closed:', msg); + } catch (closeError) { + console.error('[DEBUG] Error in comm close handler:', closeError); + } + }; + } catch (commError) { + console.error('[DEBUG] Error in comm target handler:', commError); + } + }); + + console.log('[DEBUG] Comm target registered successfully:', commTargetName); + console.log('[DEBUG] Kernel comm targets:', (kernel as any)._commTargets || 'not accessible'); + } catch (registerError) { + console.error('[DEBUG] Failed to register comm target:', registerError); + } + } catch (setupError) { + console.error('[DEBUG] Error in _setupHighlightingComm:', setupError); + } + } + + /** + * Handle highlighting data received from the kernel via comm + */ + private _handleHighlightingData(data: any): void { + try { + console.log('[DEBUG] _handleHighlightingData called'); + + if (!data) { + console.warn('[DEBUG] No highlighting data received'); + return; + } + + console.log('[DEBUG] Processing highlighting data:', data); + + // Extract highlighting information with safe fallbacks + const lines = data.lines; + const type = data.type || 'default'; + const message = data.message || ''; + + if (!lines) { + console.warn('[DEBUG] No lines property in data:', data); + return; + } + + if (!Array.isArray(lines)) { + console.warn('[DEBUG] Lines is not an array:', lines); + return; + } + + if (lines.length === 0) { + console.log('[DEBUG] No lines to highlight'); + return; + } + + console.log('[DEBUG] Highlighting lines:', lines, 'with type:', type); + + // Get the appropriate CSS class for the highlight type + const className = this._getHighlightClassName(type); + console.log('[DEBUG] Using CSS class:', className); + + // Add a small delay to ensure editor is ready + setTimeout(() => { + try { + console.log('[DEBUG] Calling highlightLines...'); + this.highlightLines(lines, className); + + if (message) { + console.log('[DEBUG] Highlighting message:', message); + } + console.log('[DEBUG] Highlighting completed successfully'); + } catch (highlightError) { + console.error('[DEBUG] Error in highlightLines:', highlightError); + } + }, 100); + } catch (error) { + console.error('[DEBUG] Error handling highlighting data:', error); + } + } + + /** + * Get the appropriate CSS class name for a highlight type + */ + private _getHighlightClassName(type: string): string { + switch (type.toLowerCase()) { + case 'error': + return 'jp-dyno-error-line'; + case 'warning': + return 'jp-dyno-warning-line'; + case 'success': + return 'jp-dyno-success-line'; + case 'info': + return 'jp-dyno-highlighted-line'; + default: + return 'jp-dyno-highlighted-line'; + } + } + /** * Safely extract nbformat outputs as JSON from a model, if available. */ @@ -369,8 +545,798 @@ dsge_report(txt=txt, filename=filename, **options)`; } } + /** + * Highlight specific lines in the associated editor + * @param lineNumbers Array of line numbers to highlight (1-based) + * @param className CSS class name for the highlight decoration + */ + highlightLines(lineNumbers: number[], className: string = 'jp-dyno-highlighted-line'): void { + console.log(`[DEBUG] highlightLines called with lines:`, lineNumbers, `className:`, className); + + if (!this._editorWidget || !this._editorWidget.content || !this._editorWidget.content.editor) { + console.warn('[DEBUG] Editor not available for highlighting'); + return; + } + + const editor = this._editorWidget.content.editor; + + // Clear existing highlights first + this.clearHighlights(); + + // Store the highlighting info for later reference + if (!this._persistentHighlights) { + this._persistentHighlights = new Map(); + } + + try { + // For JupyterLab 4.x with CodeMirror 6, we need to use a different approach + // Try to get the CodeMirror editor view + const editorView = (editor as any).editor; + + if (editorView && editorView.state && editorView.dispatch) { + console.log('[DEBUG] Using CodeMirror 6 style highlighting'); + + // Create line-level decorations using CodeMirror 6 API + const decorations: any[] = []; + + lineNumbers.forEach(lineNum => { + this._persistentHighlights.set(lineNum, className); + + // Convert to 0-based line number + const lineIndex = lineNum - 1; + + if (lineIndex >= 0 && lineIndex < editorView.state.doc.lines) { + const line = editorView.state.doc.line(lineIndex + 1); // CodeMirror lines are 1-based + + // Create a line decoration + const decoration = { + from: line.from, + to: line.to, + class: className + }; + + decorations.push(decoration); + console.log(`[DEBUG] Created decoration for line ${lineNum}: ${line.from}-${line.to}`); + } + }); + + // Store decorations for later cleanup + this._highlightMarks = decorations; + + // Apply decorations using CSS styling approach since CodeMirror 6 decorations + // are complex to add dynamically. Instead, we'll add CSS classes to line elements. + this._applyCSSLineHighlights(lineNumbers, className); + + } else { + console.log('[DEBUG] Fallback: Trying CSS-based highlighting'); + this._applyCSSLineHighlights(lineNumbers, className); + } + } catch (error) { + console.error('[DEBUG] Error in highlightLines:', error); + // Fallback to CSS-based highlighting + this._applyCSSLineHighlights(lineNumbers, className); + } + + console.log(`[DEBUG] Stored ${lineNumbers.length} highlights for lines: ${lineNumbers}`); + } + + /** + * Apply highlighting without clearing existing highlights first + * Used when applying multiple highlight types from MIME data + * @param lineNumbers Array of line numbers to highlight (1-based) + * @param className CSS class name for the highlight decoration + */ + private _applyHighlightingWithoutClear(lineNumbers: number[], className: string): void { + console.log(`[DEBUG] _applyHighlightingWithoutClear called with lines:`, lineNumbers, `className:`, className); + + if (!this._editorWidget || !this._editorWidget.content || !this._editorWidget.content.editor) { + console.warn('[DEBUG] Editor not available for highlighting'); + return; + } + + const editor = this._editorWidget.content.editor; + + // Store the highlighting info for later reference + if (!this._persistentHighlights) { + this._persistentHighlights = new Map(); + } + + try { + // For JupyterLab 4.x with CodeMirror 6, we need to use a different approach + // Try to get the CodeMirror editor view + const editorView = (editor as any).editor; + + if (editorView && editorView.state && editorView.dispatch) { + console.log('[DEBUG] Using CodeMirror 6 style highlighting'); + + // Create line-level decorations using CodeMirror 6 API + const decorations: any[] = []; + + lineNumbers.forEach(lineNum => { + this._persistentHighlights.set(lineNum, className); + + // Convert to 0-based line number + const lineIndex = lineNum - 1; + + if (lineIndex >= 0 && lineIndex < editorView.state.doc.lines) { + const line = editorView.state.doc.line(lineIndex + 1); // CodeMirror lines are 1-based + + // Create a line decoration + const decoration = { + from: line.from, + to: line.to, + class: className + }; + + decorations.push(decoration); + console.log(`[DEBUG] Created decoration for line ${lineNum}: ${line.from}-${line.to}`); + } + }); + + // Store decorations for later cleanup + this._highlightMarks.push(...decorations); + + // Apply decorations using CSS styling approach since CodeMirror 6 decorations + // are complex to add dynamically. Instead, we'll add CSS classes to line elements. + this._applyCSSLineHighlights(lineNumbers, className); + + } else { + console.log('[DEBUG] Fallback: Trying CSS-based highlighting'); + this._applyCSSLineHighlights(lineNumbers, className); + } + } catch (error) { + console.error('[DEBUG] Error in _applyHighlightingWithoutClear:', error); + // Fallback to CSS-based highlighting + this._applyCSSLineHighlights(lineNumbers, className); + } + + console.log(`[DEBUG] Applied ${lineNumbers.length} highlights for lines: ${lineNumbers}`); + } + + /** + * Apply CSS-based line highlighting as a fallback method + */ + private _applyCSSLineHighlights(lineNumbers: number[], className: string): void { + try { + const editor = this._editorWidget?.content?.editor; + if (!editor) { + console.warn('[DEBUG] No editor available for CSS highlighting'); + return; + } + + // Get the editor's DOM element using multiple possible paths + let editorElement = null; + + // Try different ways to get the editor element + if ((editor as any).host) { + editorElement = (editor as any).host; + } else if (this._editorWidget.content && (this._editorWidget.content as any).node) { + editorElement = (this._editorWidget.content as any).node; + } else if (this._editorWidget.node) { + editorElement = this._editorWidget.node; + } + + if (editorElement) { + console.log('[DEBUG] Found editor element, looking for line elements...'); + + // Look for different types of line elements that might exist + const possibleSelectors = [ + '.cm-line', // CodeMirror 6 + '.CodeMirror-line', // CodeMirror 5 + '.cm-activeLine', // CodeMirror active line + '[role="textbox"] > div', // Generic approach + '.cm-content > div' // CodeMirror 6 content + ]; + + let lineElements: NodeListOf | null = null; + let usedSelector = ''; + + for (const selector of possibleSelectors) { + const elements = editorElement.querySelectorAll(selector); + if (elements.length > 0) { + lineElements = elements; + usedSelector = selector; + console.log(`[DEBUG] Found ${elements.length} line elements using selector "${selector}"`); + break; + } + } + + if (lineElements && lineElements.length > 0) { + console.log(`[DEBUG] Using ${lineElements.length} line elements from selector "${usedSelector}"`); + + lineNumbers.forEach(lineNum => { + const lineIndex = lineNum - 1; // Convert to 0-based + + if (lineIndex >= 0 && lineIndex < lineElements.length) { + const lineElement = lineElements[lineIndex]; + if (lineElement) { + lineElement.classList.add(className); + this._highlightMarks.push({ + element: lineElement, + className: className, + lineNumber: lineNum + }); + console.log(`[DEBUG] Applied CSS class ${className} to line ${lineNum} using selector "${usedSelector}"`); + } + } else { + console.warn(`[DEBUG] Line ${lineNum} is out of bounds (max: ${lineElements.length})`); + } + }); + } else { + console.warn('[DEBUG] Could not find any line elements in editor DOM'); + console.log('[DEBUG] Editor element structure:', editorElement.innerHTML.substring(0, 500)); + + // As a last resort, try to add highlighting by observing the editor + this._attemptDelayedHighlighting(lineNumbers, className); + } + } else { + console.warn('[DEBUG] Could not find editor DOM element'); + this._attemptDelayedHighlighting(lineNumbers, className); + } + } catch (error) { + console.error('[DEBUG] Error in CSS highlighting:', error); + } + } + + /** + * Attempt delayed highlighting as a last resort + */ + private _attemptDelayedHighlighting(lineNumbers: number[], className: string): void { + console.log('[DEBUG] Attempting delayed highlighting...'); + + let retryCount = 0; + const maxRetries = 10; + + const retryHighlighting = () => { + retryCount++; + + if (retryCount > maxRetries) { + console.warn('[DEBUG] Max retries reached for delayed highlighting'); + return; + } + + console.log(`[DEBUG] Retry ${retryCount}: Looking for line elements...`); + + try { + const editor = this._editorWidget?.content?.editor; + if (!editor) { + setTimeout(retryHighlighting, 500); + return; + } + + const editorElement = (editor as any).host || (this._editorWidget.content as any).node || this._editorWidget.node; + if (!editorElement) { + setTimeout(retryHighlighting, 500); + return; + } + + const lineElements = editorElement.querySelectorAll('.cm-line, .CodeMirror-line'); + if (lineElements.length === 0) { + setTimeout(retryHighlighting, 500); + return; + } + + console.log(`[DEBUG] Retry ${retryCount}: Found ${lineElements.length} line elements`); + + // Apply highlighting + lineNumbers.forEach(lineNum => { + const lineIndex = lineNum - 1; + + if (lineIndex >= 0 && lineIndex < lineElements.length) { + const lineElement = lineElements[lineIndex]; + if (lineElement) { + lineElement.classList.add(className); + this._highlightMarks.push({ + element: lineElement, + className: className, + lineNumber: lineNum + }); + console.log(`[DEBUG] Delayed highlighting applied to line ${lineNum}`); + } + } + }); + + } catch (error) { + console.error(`[DEBUG] Error in retry ${retryCount}:`, error); + setTimeout(retryHighlighting, 500); + } + }; + + // Start the retry process + setTimeout(retryHighlighting, 500); + } + + /** + * Clear all line highlights + */ + clearHighlights(): void { + console.log('[DEBUG] Clearing highlights...'); + + // Clear stored highlight data + if (this._persistentHighlights) { + this._persistentHighlights.clear(); + } + + // Clear highlight marks + if (this._highlightMarks && this._highlightMarks.length > 0) { + this._highlightMarks.forEach(mark => { + try { + if (mark && typeof mark.clear === 'function') { + // CodeMirror style line class handle + mark.clear(); + } else if (mark && mark.element && mark.className) { + // CSS-based highlighting - remove the class + mark.element.classList.remove(mark.className); + console.log(`[DEBUG] Removed CSS class ${mark.className} from line ${mark.lineNumber}`); + } + } catch (error) { + console.warn('[DEBUG] Error clearing highlight mark:', error); + } + }); + this._highlightMarks = []; + } + + // Additional cleanup: remove all our highlight classes from the editor DOM + const editor = this._editorWidget?.content?.editor; + if (editor) { + const editorElement = (editor as any).host || (this._editorWidget.content as any).node; + + if (editorElement) { + const highlightClasses = [ + 'jp-dyno-highlighted-line', + 'jp-dyno-error-line', + 'jp-dyno-warning-line', + 'jp-dyno-success-line' + ]; + + // Remove highlight classes from all line elements + highlightClasses.forEach(className => { + const highlightedElements = editorElement.querySelectorAll(`.${className}`); + highlightedElements.forEach((element: Element) => { + element.classList.remove(className); + }); + }); + + console.log('[DEBUG] Cleaned up CSS classes from editor DOM'); + } + } + + console.log('[DEBUG] Cleared all highlights'); + } + + /** + * Set the reference to the associated editor widget + */ + setEditorWidget(editorWidget: any): void { + console.log('[DEBUG] setEditorWidget called with:', editorWidget); + this._editorWidget = editorWidget; + + // Set the correct MIME type for the editor immediately + this._setEditorMimeType(); + + // Wait for the editor to be fully loaded + this._waitForEditorReady().then(() => { + console.log('[DEBUG] Editor is ready for highlighting'); + + // Apply syntax highlighting + this._applySyntaxHighlighting(); + + // Don't automatically test highlighting - let it be triggered by actual data + }).catch(error => { + console.warn('[DEBUG] Error waiting for editor ready:', error); + }); + } + + /** + * Force set the language mode via JupyterLab's language registry + */ + private _forceLanguageMode(filePath: string): void { + console.log('[DEBUG] Forcing language mode for:', filePath); + + try { + const editor = this._editorWidget.content.editor; + let languageName = ''; + + if (filePath.endsWith('.dyno') || filePath.endsWith('.πŸ¦–') || filePath.endsWith('.dyno.yaml')) { + languageName = 'dyno'; + } else if (filePath.endsWith('.mod') || filePath.endsWith('.dynare.mod')) { + languageName = 'mod'; + } + + if (languageName) { + // Try to get the editor's language support and force set it + const editorView = (editor as any).editor; + if (editorView && editorView.state) { + console.log('[DEBUG] Attempting to force language mode:', languageName); + + // Check if the language is already applied + const currentLang = editorView.state.facet ? 'has facets' : 'no facets'; + console.log('[DEBUG] Current editor language state:', currentLang); + + // Try to get language from global registry + if ((window as any).jupyterlab) { + const app = (window as any).jupyterlab; + if (app.serviceManager && app.serviceManager.language) { + console.log('[DEBUG] Found language service, attempting to set language'); + // This would be the ideal way but may not be available + } + } + } + } + } catch (error) { + console.warn('[DEBUG] Failed to force language mode:', error); + } + } + + /** + * Set the correct MIME type for the editor based on file extension + */ + private _setEditorMimeType(): void { + if (!this._editorWidget || !this._editorWidget.content || !this._editorWidget.content.editor) { + return; + } + + const filePath = this._editorWidget.context?.path || ''; + let mimeType = ''; + + console.log('[DEBUG] Setting MIME type for file:', filePath); + + if (filePath.endsWith('.dyno') || filePath.endsWith('.πŸ¦–') || filePath.endsWith('.dyno.yaml')) { + mimeType = 'text/x-dyno'; + } else if (filePath.endsWith('.mod') || filePath.endsWith('.dynare.mod')) { + mimeType = 'text/x-mod'; + } + + if (mimeType) { + try { + const editor = this._editorWidget.content.editor; + console.log('[DEBUG] Setting editor MIME type to:', mimeType); + + // For JupyterLab 4.x, try to set the mode directly + if ((editor as any).setOption) { + (editor as any).setOption('mode', mimeType); + } else if ((editor as any).model && (editor as any).model.mimeType) { + (editor as any).model.mimeType = mimeType; + } + + // Also try to set it via the document model + if (this._editorWidget.context && this._editorWidget.context.model) { + (this._editorWidget.context.model as any).mimeType = mimeType; + } + + console.log('[DEBUG] Editor MIME type set successfully'); + } catch (error) { + console.warn('[DEBUG] Failed to set editor MIME type:', error); + } + } + } + + /** + * Apply syntax highlighting to the editor + */ + private _applySyntaxHighlighting(): void { + if (!this._editorWidget || !this._editorWidget.content || !this._editorWidget.content.editor) { + console.warn('[DEBUG] No editor available for syntax highlighting'); + return; + } + + try { + const editor = this._editorWidget.content.editor; + const filePath = this._editorWidget.context?.path || ''; + + console.log('[DEBUG] Applying syntax highlighting for file:', filePath); + + // First, try to force set the language via JupyterLab's language registry + this._forceLanguageMode(filePath); + + // Determine which language mode to use based on file extension + let languageMode = null; + if (filePath.endsWith('.dyno') || filePath.endsWith('.πŸ¦–') || filePath.endsWith('.dyno.yaml')) { + languageMode = dyno(); + console.log('[DEBUG] Using DYNO language mode'); + } else if (filePath.endsWith('.mod') || filePath.endsWith('.dynare.mod')) { + languageMode = mod(); + console.log('[DEBUG] Using MOD language mode'); + } + + if (languageMode) { + // Try multiple approaches to apply the language mode + const editorView = (editor as any).editor; + console.log('[DEBUG] Editor view:', editorView); + + if (editorView && editorView.state && editorView.dispatch) { + console.log('[DEBUG] Attempting to apply language mode via CodeMirror 6 API'); + + try { + // Approach 1: Try direct language reconfiguration + import('@codemirror/state').then(({ Compartment, StateEffect }) => { + console.log('[DEBUG] Loaded CodeMirror state module'); + + if (!this._languageCompartment) { + this._languageCompartment = new Compartment(); + + // Apply initial configuration + const transaction = editorView.state.update({ + effects: this._languageCompartment.reconfigure(languageMode) + }); + editorView.dispatch(transaction); + console.log('[DEBUG] Language mode applied with new compartment'); + } else { + // Reconfigure existing + const transaction = editorView.state.update({ + effects: this._languageCompartment.reconfigure(languageMode) + }); + editorView.dispatch(transaction); + console.log('[DEBUG] Language mode reconfigured'); + } + }).catch(error => { + console.error('[DEBUG] Error with Compartment approach:', error); + + // Approach 2: Try extension approach + try { + const newExtensions = [languageMode]; + const transaction = editorView.state.update({ + effects: { reconfigure: newExtensions } + }); + editorView.dispatch(transaction); + console.log('[DEBUG] Language mode applied via extension reconfiguration'); + } catch (extError) { + console.error('[DEBUG] Extension approach failed:', extError); + + // Approach 3: Try replacing the entire configuration + this._tryAdvancedLanguageApplication(editorView, languageMode, filePath); + } + }); + } catch (mainError) { + console.error('[DEBUG] Main language application failed:', mainError); + this._tryAdvancedLanguageApplication(editorView, languageMode, filePath); + } + } else { + console.warn('[DEBUG] Could not access CodeMirror editor view'); + this._trySimpleSyntaxHighlighting(filePath); + } + } else { + console.log('[DEBUG] No language mode needed for file:', filePath); + } + } catch (error) { + console.error('[DEBUG] Error applying syntax highlighting:', error); + } + } + + /** + * Try advanced language application techniques + */ + private _tryAdvancedLanguageApplication(editorView: any, languageMode: any, filePath: string): void { + console.log('[DEBUG] Trying advanced language application'); + + try { + // Try getting the current document and creating a new state + const currentDoc = editorView.state.doc; + + import('@codemirror/state').then(({ EditorState }) => { + const newState = EditorState.create({ + doc: currentDoc, + extensions: [languageMode] + }); + + // Replace the entire state + editorView.setState(newState); + console.log('[DEBUG] Applied language mode by replacing editor state'); + }).catch(error => { + console.error('[DEBUG] Advanced application failed:', error); + this._trySimpleSyntaxHighlighting(filePath); + }); + } catch (error) { + console.error('[DEBUG] Advanced language application error:', error); + this._trySimpleSyntaxHighlighting(filePath); + } + } + + /** + * Fallback to simple CSS-based syntax highlighting + */ + private _trySimpleSyntaxHighlighting(filePath: string): void { + console.log('[DEBUG] Falling back to simple syntax highlighting for:', filePath); + + setTimeout(() => { + try { + const editorElement = this._editorWidget?.content?.node; + if (editorElement) { + const lines = editorElement.querySelectorAll('.cm-line, .CodeMirror-line'); + console.log(`[DEBUG] Found ${lines.length} lines for CSS highlighting`); + + // Apply simple regex-based highlighting + lines.forEach((line: Element, index: number) => { + const htmlLine = line as HTMLElement; + let content = htmlLine.textContent || ''; + + if (content.trim()) { + // Apply basic highlighting patterns + if (filePath.endsWith('.dyno') || filePath.endsWith('.πŸ¦–') || filePath.endsWith('.dyno.yaml')) { + content = this._applyDynoHighlighting(content); + } else if (filePath.endsWith('.mod') || filePath.endsWith('.dynare.mod')) { + content = this._applyModHighlighting(content); + } + + if (content !== htmlLine.textContent) { + htmlLine.innerHTML = content; + console.log(`[DEBUG] Applied CSS highlighting to line ${index + 1}`); + } + } + }); + } + } catch (error) { + console.error('[DEBUG] Simple syntax highlighting failed:', error); + } + }, 500); + } + + /** + * Apply DYNO-specific highlighting patterns + */ + private _applyDynoHighlighting(content: string): string { + return content + .replace(/\b(var|varexo|parameters|model|steady_state_model|shocks|end)\b/g, '$1') + .replace(/\b(log|exp|sin|cos|tan|sqrt|abs|max|min)\b/g, '$1') + .replace(/#.*$/gm, '$&') + .replace(/\b\d*\.?\d+([eE][+-]?\d+)?\b/g, '$&') + .replace(/<-/g, '$&'); + } + + /** + * Apply MOD-specific highlighting patterns + */ + private _applyModHighlighting(content: string): string { + return content + .replace(/\b(var|varexo|parameters|model|end|initval|steady|stoch_simul)\b/g, '$1') + .replace(/\b(log|exp|sin|cos|tan|sqrt|abs|max|min)\b/g, '$1') + .replace(/\/\/.*$/gm, '$&') + .replace(/\/\*[\s\S]*?\*\//g, '$&') + .replace(/\b\d*\.?\d+([eE][+-]?\d+)?\b/g, '$&') + .replace(/=/g, '$&'); + } + + /** + * Test highlighting functionality - can be called manually for debugging + */ + testHighlighting(): void { + console.log('[DEBUG] === TESTING HIGHLIGHTING ==='); + + try { + // Test different highlight styles + console.log('[DEBUG] Testing default highlighting on lines 1-2'); + this.highlightLines([1, 2], 'jp-dyno-highlighted-line'); + + // Test with a delay for error highlighting + setTimeout(() => { + console.log('[DEBUG] Testing error highlighting on line 3'); + this.highlightLines([3], 'jp-dyno-error-line'); + }, 2000); + + // Test with another delay for warning highlighting + setTimeout(() => { + console.log('[DEBUG] Testing warning highlighting on line 4'); + this.highlightLines([4], 'jp-dyno-warning-line'); + }, 4000); + + } catch (error) { + console.error('[DEBUG] Error in testHighlighting:', error); + } + } + + /** + * Wait for the editor to be fully ready + */ + private async _waitForEditorReady(): Promise { + return new Promise((resolve, reject) => { + const checkEditor = () => { + if (this._editorWidget && + this._editorWidget.content && + this._editorWidget.content.editor && + this._editorWidget.content.editor.host) { + + const editorElement = this._editorWidget.content.editor.host; + const lineElements = editorElement.querySelectorAll('.cm-line, .CodeMirror-line'); + + if (lineElements.length > 0) { + console.log('[DEBUG] Editor is ready with', lineElements.length, 'lines'); + resolve(); + return; + } + } + + // If not ready, check again after a short delay + setTimeout(checkEditor, 100); + }; + + checkEditor(); + + // Timeout after 10 seconds + setTimeout(() => { + reject(new Error('Editor ready timeout')); + }, 10000); + }); + } + + /** + * Perform highlighting based on rendering results + * First checks for custom MIME type data, then falls back to content analysis + */ + private _performHighlightingBasedOnResults(outputs: any[]): void { + console.log('[DEBUG] _performHighlightingBasedOnResults called with outputs:', outputs); + console.log('[DEBUG] Outputs count:', outputs.length); + + // First, check for our custom MIME type in the outputs + for (let i = 0; i < outputs.length; i++) { + const output = outputs[i]; + console.log(`[DEBUG] Output ${i}:`, output); + console.log(`[DEBUG] Output ${i} type:`, output.output_type); + + if (output.output_type === 'display_data' && output.data) { + console.log(`[DEBUG] Output ${i} data keys:`, Object.keys(output.data)); + + // Check for our custom MIME type + const mimeType = 'application/vnd.jupyterlab-dyno.highlighting+json'; + + if (mimeType in output.data) { + const highlightData = output.data[mimeType]; + console.log('[DEBUG] *** FOUND HIGHLIGHTING MIME DATA ***:', highlightData); + + // Handle array of highlight objects (new format) + if (Array.isArray(highlightData)) { + console.log('[DEBUG] *** PROCESSING ARRAY OF HIGHLIGHT OBJECTS ***'); + + // Clear existing highlights first + this.clearHighlights(); + + // Group lines by type for efficient highlighting + const linesByType = new Map(); + + highlightData.forEach(item => { + if (item.line && item.type) { + const type = item.type; + if (!linesByType.has(type)) { + linesByType.set(type, []); + } + linesByType.get(type)!.push(item.line); + console.log(`[DEBUG] Added line ${item.line} with type ${type}, message: ${item.message || 'none'}`); + } + }); + + // Apply highlighting for each type with increasing delays to avoid clearing each other + let delay = 200; + linesByType.forEach((lines, type) => { + const className = this._getHighlightClassName(type); + console.log(`[DEBUG] *** APPLYING HIGHLIGHTING ***: type=${type}, lines=${lines}, className=${className}`); + + // Apply highlighting with a delay to ensure DOM is ready + // Use a separate delay for each type and don't call clearHighlights in highlightLines + setTimeout(() => { + this._applyHighlightingWithoutClear(lines, className); + }, delay); + delay += 50; // Stagger the highlighting calls + }); + + return; // Found and applied highlighting, exit early + } + } + } + } + + console.log('[DEBUG] No MIME highlighting data found. No fallback highlighting will be applied.'); + console.log('[DEBUG] Highlighting check completed - only MIME type highlighting is active.'); + } + + + // Dispose of resources held by the widget dispose(): void { + // Clear any line highlights + this.clearHighlights(); + + // Clean up comm targets + this._cleanupComm(); + // Disconnect activity monitor first if (this._monitor) { this._monitor.dispose(); @@ -411,6 +1377,28 @@ dsge_report(txt=txt, filename=filename, **options)`; null; private _rendermime: IRenderMimeRegistry; private _isFirstRender = true; + /** + * Clean up comm targets when disposing + */ + private _cleanupComm(): void { + if (this._sessionContext?.session?.kernel) { + try { + // Note: removeCommTarget may not exist or may have different signature + const kernel = this._sessionContext.session.kernel; + if (typeof (kernel as any).removeCommTarget === 'function') { + (kernel as any).removeCommTarget('jupyterlab-dyno-highlighting', () => {}); + } + console.log('[DEBUG] Comm target cleaned up'); + } catch (e) { + // Ignore if target doesn't exist or method signature is different + } + } + } + + private _editorWidget: any = null; // Reference to the associated editor widget + private _highlightMarks: any[] = []; // Store highlight marks for cleanup + private _persistentHighlights: Map = new Map(); // Store persistent highlight info + private _languageCompartment: any = null; // CodeMirror language compartment } /** @@ -465,18 +1453,61 @@ const FACTORY = 'Dyno extension'; /** * Initialization data for the jupyterlab-dyno extension. */ -const plugin: JupyterFrontEndPlugin = { +const plugin: JupyterFrontEndPlugin> = { id: PLUGIN_ID, description: 'A JupyterLab extension for solving DSGE models', autoStart: true, - requires: [ILayoutRestorer, IRenderMimeRegistry, ISettingRegistry], + provides: IDynareTracker, + requires: [ILayoutRestorer, IRenderMimeRegistry, ISettingRegistry, IEditorLanguageRegistry], + optional: [], activate: ( app: JupyterFrontEnd, restorer: ILayoutRestorer, rendermime: IRenderMimeRegistry, - settings: ISettingRegistry - ) => { + settings: ISettingRegistry, + editorLanguages: IEditorLanguageRegistry + ): IWidgetTracker => { console.log('JupyterLab extension jupyterlab-dyno is activated!'); + + // Register syntax highlighting for our custom languages with JupyterLab's editor system + console.log('Registering DYNO and MOD language modes with editor language registry...'); + + try { + // Register DYNO language mode + editorLanguages.addLanguage({ + name: 'dyno', + mime: 'text/x-dyno', + load: async () => { + console.log('[DEBUG] Loading DYNO language mode'); + return dyno(); + } + }); + + // Register MOD language mode + editorLanguages.addLanguage({ + name: 'mod', + mime: 'text/x-mod', + load: async () => { + console.log('[DEBUG] Loading MOD language mode'); + return mod(); + } + }); + + // Also register for the existing MIME type used by the extension + editorLanguages.addLanguage({ + name: 'dyno-alt', + mime: 'text/mod', // This is your existing MIME type + load: async () => { + console.log('[DEBUG] Loading DYNO language mode for text/mod MIME type'); + return dyno(); + } + }); + + console.log('Language modes registered successfully'); + } catch (error) { + console.error('Error registering language modes:', error); + } + const { commands, shell } = app; // Tracker const namespace = 'jupyterlab-dyno'; @@ -512,6 +1543,10 @@ const plugin: JupyterFrontEndPlugin = { // Add widget to tracker when created widgetFactory.widgetCreated.connect(async (sender, widget) => { + // Make the widget globally accessible for debugging + (window as any).currentDynoWidget = widget; + console.log('[DEBUG] Widget is now accessible as window.currentDynoWidget'); + // Notify instance tracker if restore data needs to be updated widget.context.pathChanged.connect(() => { tracker.save(widget); @@ -537,6 +1572,9 @@ const plugin: JupyterFrontEndPlugin = { splitDone = true; leftEditorRefId = editor.id; rightViewerRefId = widget.id; + + // Store editor reference in the widget for highlighting + widget.setEditorWidget(editor); } else { if (rightViewerRefId) { shell.add(widget, 'main', { @@ -545,11 +1583,14 @@ const plugin: JupyterFrontEndPlugin = { }); } if (leftEditorRefId) { - await commands.execute('docmanager:open', { + const editor = await commands.execute('docmanager:open', { path: widget.context.path, factory: 'Editor', options: { mode: 'tab-after', ref: leftEditorRefId } }); + + // Store editor reference in the widget for highlighting + widget.setEditorWidget(editor); } } @@ -562,7 +1603,10 @@ const plugin: JupyterFrontEndPlugin = { global_setting = setting.composite as any; preserveScrollPosition = (setting.get('preserve-scroll-position') .composite as boolean) ?? true; - console.log(global_setting); + console.log('Settings loaded:', { + preserveScrollPosition, + global_setting + }); } /** @@ -580,14 +1624,14 @@ const plugin: JupyterFrontEndPlugin = { // Register widget and model factories app.docRegistry.addWidgetFactory(widgetFactory); - // Register file type + // Register file types with proper MIME types for syntax highlighting app.docRegistry.addFileType({ name: 'mod', displayName: 'Mod', extensions: ['.mod', '.dynare.mod'], fileFormat: 'text', contentType: 'file', - mimeTypes: [MIME_TYPE] + mimeTypes: ['text/x-mod', MIME_TYPE] }); app.docRegistry.addFileType({ name: 'dyno', @@ -595,7 +1639,7 @@ const plugin: JupyterFrontEndPlugin = { extensions: ['.dyno', '.πŸ¦–'], fileFormat: 'text', contentType: 'file', - mimeTypes: [MIME_TYPE] + mimeTypes: ['text/x-dyno', MIME_TYPE] }); app.docRegistry.addFileType({ name: 'dynoYAML', @@ -603,8 +1647,10 @@ const plugin: JupyterFrontEndPlugin = { extensions: ['.dyno.yaml'], fileFormat: 'text', contentType: 'file', - mimeTypes: [MIME_TYPE] + mimeTypes: ['text/x-dyno', MIME_TYPE] }); + + return tracker; } }; diff --git a/src/languages/dyno-language.ts b/src/languages/dyno-language.ts new file mode 100644 index 0000000..3936a8c --- /dev/null +++ b/src/languages/dyno-language.ts @@ -0,0 +1,190 @@ +import { LanguageSupport, StreamLanguage } from '@codemirror/language'; + +// Mode definition for DYNO syntax highlighting +export const dynoMode = { + name: 'dyno', + startState: () => ({ inComment: false }), + token: (stream: any) => { + // Comments starting with # + if (stream.match(/^#.*/)) { + return 'comment'; + } + + // Section headers (comments that define sections) + if (stream.match(/^#\s*(parameters|equations|steady state|shocks)/i)) { + return 'meta'; + } + + // Keywords for model blocks + if (stream.match(/\b(var|varexo|parameters|model|steady_state_model|shocks|end)\b/)) { + return 'keyword'; + } + + // Mathematical functions + if (stream.match(/\b(log|exp|sin|cos|tan|sqrt|abs|max|min)\b/)) { + return 'builtin'; + } + + // Parameter assignment arrow + if (stream.match(/<-/)) { + return 'operator'; + } + + // Numbers (integers, decimals, scientific notation) + if (stream.match(/\b\d*\.?\d+([eE][+-]?\d+)?\b/)) { + return 'number'; + } + + // Time subscripts content (t, t+1, t-1, ~, 1, 2, etc.) - match the content inside brackets + if (stream.match(/\b([t~]([+\-]\d+)?|\d+)\b/)) { + return 'time-subscript'; + } + + // Opening and closing brackets (will inherit variable color when following variables) + if (stream.match(/[\[\]]/)) { + return 'bracket'; + } + + // Common economic variables (can be customized) + if (stream.match(/\b(c|k|y|n|r|w|i|a|beta|delta|alpha|rho|khi|eta|nss|epsilon|leta)\b/)) { + return 'variable'; + } + + // Distribution notation for shocks N(0, sigma) + if (stream.match(/\bN\(/)) { + return 'builtin'; + } + + // Operators and punctuation + if (stream.match(/[+\-*/=<>^()[\]{}]/)) { + return 'operator'; + } + + // Skip whitespace + if (stream.match(/\s+/)) { + return null; + } + + // Identifiers (variables not in the common list) + if (stream.match(/[a-zA-Z_]\w*/)) { + return 'variable-2'; + } + + // Skip any unrecognized character + stream.next(); + return null; + }, + + languageData: { + commentTokens: { line: '#' }, + indentOnInput: /^\s*end\s*$/, + closeBrackets: { brackets: ['(', '[', '{', '"', "'"] } + } +}; + +// Mode definition for MOD files (Dynare syntax) +export const modMode = { + name: 'mod', + startState: () => ({ inComment: false, inBlock: null }), + token: (stream: any, state: any) => { + // Block comments /* ... */ + if (state.inComment) { + if (stream.match(/.*?\*\//)) { + state.inComment = false; + return 'comment'; + } + stream.skipToEnd(); + return 'comment'; + } + + if (stream.match(/\/\*/)) { + state.inComment = true; + return 'comment'; + } + + // Line comments // + if (stream.match(/\/\/.*/)) { + return 'comment'; + } + + // Dynare block keywords + if (stream.match(/\b(var|varexo|varendo|parameters|model|initval|endval|steady_state_model|shocks|estimated_params|end)\b/)) { + const word = stream.current(); + if (word === 'model' || word === 'steady_state_model' || word === 'shocks') { + state.inBlock = word; + } else if (word === 'end') { + state.inBlock = null; + } + return 'keyword'; + } + + // Mathematical functions + if (stream.match(/\b(log|exp|sin|cos|tan|sqrt|abs|max|min|steady_state|normcdf|normpdf)\b/)) { + return 'builtin'; + } + + // Numbers + if (stream.match(/\b\d*\.?\d+([eE][+-]?\d+)?\b/)) { + return 'number'; + } + + // Time subscripts for MOD files: (+1), (-1) or [t], [t+1], etc. + if (stream.match(/\([+\-]\d+\)/)) { + return 'time-subscript'; + } + + // Time subscripts content for bracket notation (t, t+1, t-1, ~, 1, 2, etc.) + if (stream.match(/\b([t~]([+\-]\d+)?|\d+)\b/)) { + return 'time-subscript'; + } + + // Opening and closing brackets + if (stream.match(/[\[\]]/)) { + return 'bracket'; + } + + // Assignment and comparison operators + if (stream.match(/[=<>]=?|<-/)) { + return 'operator'; + } + + // Arithmetic operators + if (stream.match(/[+\-*/^]/)) { + return 'operator'; + } + + // Punctuation + if (stream.match(/[()[\]{},.;]/)) { + return 'punctuation'; + } + + // Variables + if (stream.match(/[a-zA-Z_]\w*/)) { + return 'variable'; + } + + // Skip whitespace + if (stream.match(/\s+/)) { + return null; + } + + // Skip any unrecognized character + stream.next(); + return null; + }, + + languageData: { + commentTokens: { line: '//', block: { open: '/*', close: '*/' } }, + indentOnInput: /^\s*end\s*$/, + closeBrackets: { brackets: ['(', '[', '{', '"', "'"] } + } +}; + +// Create language supports +export function dyno(): LanguageSupport { + return new LanguageSupport(StreamLanguage.define(dynoMode)); +} + +export function mod(): LanguageSupport { + return new LanguageSupport(StreamLanguage.define(modMode)); +} \ No newline at end of file diff --git a/src/languages/index.ts b/src/languages/index.ts new file mode 100644 index 0000000..086390c --- /dev/null +++ b/src/languages/index.ts @@ -0,0 +1,34 @@ +import { IEditorLanguageRegistry } from '@jupyterlab/codemirror'; +import { dyno, mod } from './dyno-language'; + +/** + * Register the custom language modes with JupyterLab's editor system + */ +export function registerLanguages(editorLanguages: IEditorLanguageRegistry): void { + // Register DYNO language mode + editorLanguages.addLanguage({ + name: 'dyno', + mime: 'text/x-dyno', + load: async () => { + return dyno(); + } + }); + + // Register MOD language mode + editorLanguages.addLanguage({ + name: 'mod', + mime: 'text/x-mod', + load: async () => { + return mod(); + } + }); + + // Also register for the existing MIME type + editorLanguages.addLanguage({ + name: 'dyno-alt', + mime: 'text/mod', // This is your existing MIME type + load: async () => { + return dyno(); + } + }); +} \ No newline at end of file diff --git a/style/base.css b/style/base.css index 278a8f6..2d1f508 100644 --- a/style/base.css +++ b/style/base.css @@ -48,8 +48,201 @@ text-align: center; } +/* Line highlighting styles */ +.jp-dyno-highlighted-line { + background-color: rgba(255, 235, 59, 0.3) !important; /* Light yellow highlight */ + border-left: 3px solid #ffc107 !important; /* Golden left border */ + transition: background-color 0.2s ease-in-out; +} + +.jp-dyno-highlighted-line:hover { + background-color: rgba(255, 235, 59, 0.4) !important; /* Slightly more opaque on hover */ +} + +/* Alternative highlight styles for different types of highlighting */ +.jp-dyno-error-line { + background-color: rgba(244, 67, 54, 0.1) !important; /* Light red for errors */ + border-left: 3px solid #f44336 !important; +} + +.jp-dyno-warning-line { + background-color: rgba(255, 152, 0, 0.1) !important; /* Light orange for warnings */ + border-left: 3px solid #ff9800 !important; +} + +.jp-dyno-success-line { + background-color: rgba(76, 175, 80, 0.1) !important; /* Light green for success */ + border-left: 3px solid #4caf50 !important; +} + +/* Ensure highlighting works with CodeMirror 6 line elements */ +.cm-line.jp-dyno-highlighted-line, +.CodeMirror-line.jp-dyno-highlighted-line { + background-color: rgba(255, 235, 59, 0.3) !important; + border-left: 3px solid #ffc107 !important; +} + +.cm-line.jp-dyno-error-line, +.CodeMirror-line.jp-dyno-error-line { + background-color: rgba(244, 67, 54, 0.1) !important; + border-left: 3px solid #f44336 !important; +} + +.cm-line.jp-dyno-warning-line, +.CodeMirror-line.jp-dyno-warning-line { + background-color: rgba(255, 152, 0, 0.1) !important; + border-left: 3px solid #ff9800 !important; +} + +.cm-line.jp-dyno-success-line, +.CodeMirror-line.jp-dyno-success-line { + background-color: rgba(76, 175, 80, 0.1) !important; + border-left: 3px solid #4caf50 !important; +} + +/* Ensure highlighting persists during selection and focus */ +.cm-focused .cm-line.jp-dyno-highlighted-line, +.CodeMirror-focused .CodeMirror-line.jp-dyno-highlighted-line { + background-color: rgba(255, 235, 59, 0.3) !important; + border-left: 3px solid #ffc107 !important; +} + +.cm-focused .cm-line.jp-dyno-error-line, +.CodeMirror-focused .CodeMirror-line.jp-dyno-error-line { + background-color: rgba(244, 67, 54, 0.1) !important; + border-left: 3px solid #f44336 !important; +} + +.cm-focused .cm-line.jp-dyno-warning-line, +.CodeMirror-focused .CodeMirror-line.jp-dyno-warning-line { + background-color: rgba(255, 152, 0, 0.1) !important; + border-left: 3px solid #ff9800 !important; +} + +.cm-focused .cm-line.jp-dyno-success-line, +.CodeMirror-focused .CodeMirror-line.jp-dyno-success-line { + background-color: rgba(76, 175, 80, 0.1) !important; + border-left: 3px solid #4caf50 !important; +} + /* Make sure the widget container is positioned relative for absolute positioning */ .jupyterlab-dyno { position: relative; min-height: 200px; } + +/* Syntax highlighting styles for DYNO and MOD languages */ + +/* Comments */ +.cm-editor .cm-line .cm-comment { + color: #6a9955; + font-style: italic; +} + +/* Keywords (var, model, end, etc.) */ +.cm-editor .cm-line .cm-keyword { + color: #569cd6; + font-weight: bold; +} + +/* Built-in functions (log, exp, etc.) */ +.cm-editor .cm-line .cm-builtin { + color: #4ec9b0; + font-weight: 500; +} + +/* Numbers */ +.cm-editor .cm-line .cm-number { + color: #b5cea8; +} + +/* Variables */ +.cm-editor .cm-line .cm-variable { + color: #9cdcfe; +} + +/* Secondary variables (user-defined) */ +.cm-editor .cm-line .cm-variable-2 { + color: #dcdcaa; +} + +/* Operators */ +.cm-editor .cm-line .cm-operator { + color: #d4d4d4; +} + +/* Special atoms (time subscripts [t], [t+1], etc.) */ +.cm-editor .cm-line .cm-atom { + color: #ffd700; + font-weight: bold; +} + +/* Time subscripts (t, t+1, t-1, ~, (+1), (-1)) */ +.cm-editor .cm-line .cm-time-subscript { + color: #ff6b6b; + font-weight: bold; +} + +/* Brackets (inherit variable color) */ +.cm-editor .cm-line .cm-bracket { + color: #9cdcfe; + font-weight: normal; +} + +/* Meta (section headers) */ +.cm-editor .cm-line .cm-meta { + color: #c586c0; + font-weight: bold; +} + +/* Punctuation */ +.cm-editor .cm-line .cm-punctuation { + color: #cccccc; +} + +/* Light theme syntax highlighting */ +[data-jp-theme-light] .cm-editor .cm-line .cm-comment { + color: #008000; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-keyword { + color: #0000ff; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-builtin { + color: #795e26; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-number { + color: #098658; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-variable { + color: #001080; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-variable-2 { + color: #795e26; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-operator { + color: #000000; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-atom { + color: #0451a5; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-time-subscript { + color: #d63384; + font-weight: bold; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-bracket { + color: #001080; + font-weight: normal; +} + +[data-jp-theme-light] .cm-editor .cm-line .cm-meta { + color: #800080; +} diff --git a/test.mod b/test.mod index 571a257..ff079fa 100644 --- a/test.mod +++ b/test.mod @@ -2,7 +2,7 @@ var y, c, k, a, h, b; varexo e, u; parameters beta, rho, alpha, delta, theta, psi, tau; - + alpha = 0.36; rho = 0.95; tau = 0.025; diff --git a/tsconfig.json b/tsconfig.json index 25af040..aab1342 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,5 +21,5 @@ "target": "ES2018", "types": ["jest"] }, - "include": ["src/*"] + "include": ["src/**/*"] } diff --git a/yarn.lock b/yarn.lock index 77b8e22..2d95ed0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1521,7 +1521,7 @@ __metadata: languageName: node linkType: hard -"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0, @codemirror/state@npm:^6.5.2": +"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.2.0, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0, @codemirror/state@npm:^6.5.2": version: 6.5.2 resolution: "@codemirror/state@npm:6.5.2" dependencies: @@ -2078,6 +2078,35 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/apputils@npm:^4.5.9": + version: 4.5.9 + resolution: "@jupyterlab/apputils@npm:4.5.9" + dependencies: + "@jupyterlab/coreutils": ^6.4.9 + "@jupyterlab/observables": ^5.4.9 + "@jupyterlab/rendermime-interfaces": ^3.12.9 + "@jupyterlab/services": ^7.4.9 + "@jupyterlab/settingregistry": ^4.4.9 + "@jupyterlab/statedb": ^4.4.9 + "@jupyterlab/statusbar": ^4.4.9 + "@jupyterlab/translation": ^4.4.9 + "@jupyterlab/ui-components": ^4.4.9 + "@lumino/algorithm": ^2.0.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/domutils": ^2.0.3 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/virtualdom": ^2.0.3 + "@lumino/widgets": ^2.7.1 + "@types/react": ^18.0.26 + react: ^18.2.0 + sanitize-html: ~2.12.1 + checksum: 19c75e607ca9c5422cb0128d8c4c279d7c75d3d9681c9d45112e0291323279034550f19754a203ed0c09c3dc51b3eb16ee2c0c29be1777dddcf499ef4e6d4b5f + languageName: node + linkType: hard + "@jupyterlab/attachments@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/attachments@npm:4.4.7" @@ -2193,6 +2222,72 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/codeeditor@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/codeeditor@npm:4.4.9" + dependencies: + "@codemirror/state": ^6.5.2 + "@jupyter/ydoc": ^3.1.0 + "@jupyterlab/apputils": ^4.5.9 + "@jupyterlab/coreutils": ^6.4.9 + "@jupyterlab/nbformat": ^4.4.9 + "@jupyterlab/observables": ^5.4.9 + "@jupyterlab/statusbar": ^4.4.9 + "@jupyterlab/translation": ^4.4.9 + "@jupyterlab/ui-components": ^4.4.9 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/dragdrop": ^2.1.6 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/widgets": ^2.7.1 + react: ^18.2.0 + checksum: 1e1e374a3fadf12c30b6239b20d19c00e704403c5ac1dc70f9a168f460bf67610525b4f3c4da606ada17eff5940fe28cf022415d6bd3aa1714751e00a0eddfe4 + languageName: node + linkType: hard + +"@jupyterlab/codemirror@npm:^4.4.5": + version: 4.4.9 + resolution: "@jupyterlab/codemirror@npm:4.4.9" + dependencies: + "@codemirror/autocomplete": ^6.18.6 + "@codemirror/commands": ^6.8.1 + "@codemirror/lang-cpp": ^6.0.2 + "@codemirror/lang-css": ^6.3.1 + "@codemirror/lang-html": ^6.4.9 + "@codemirror/lang-java": ^6.0.1 + "@codemirror/lang-javascript": ^6.2.3 + "@codemirror/lang-json": ^6.0.1 + "@codemirror/lang-markdown": ^6.3.2 + "@codemirror/lang-php": ^6.0.1 + "@codemirror/lang-python": ^6.2.0 + "@codemirror/lang-rust": ^6.0.1 + "@codemirror/lang-sql": ^6.8.0 + "@codemirror/lang-wast": ^6.0.2 + "@codemirror/lang-xml": ^6.1.0 + "@codemirror/language": ^6.11.0 + "@codemirror/legacy-modes": ^6.5.1 + "@codemirror/search": ^6.5.10 + "@codemirror/state": ^6.5.2 + "@codemirror/view": ^6.38.1 + "@jupyter/ydoc": ^3.1.0 + "@jupyterlab/codeeditor": ^4.4.9 + "@jupyterlab/coreutils": ^6.4.9 + "@jupyterlab/documentsearch": ^4.4.9 + "@jupyterlab/nbformat": ^4.4.9 + "@jupyterlab/translation": ^4.4.9 + "@lezer/common": ^1.2.1 + "@lezer/generator": ^1.7.0 + "@lezer/highlight": ^1.2.0 + "@lezer/markdown": ^1.3.0 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/signaling": ^2.1.4 + yjs: ^13.5.40 + checksum: 68cc6f265c990e33b2fd4122ca28cc7fc5b039d8a829a7513415ac7245ce9a9ff0183515e48b1bac0085507bddfd5666e45ae8fbfef6de90347a421d4341e4a4 + languageName: node + linkType: hard + "@jupyterlab/codemirror@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/codemirror@npm:4.4.7" @@ -2249,6 +2344,20 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/coreutils@npm:^6.4.9": + version: 6.4.9 + resolution: "@jupyterlab/coreutils@npm:6.4.9" + dependencies: + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/signaling": ^2.1.4 + minimist: ~1.2.0 + path-browserify: ^1.0.0 + url-parse: ~1.5.4 + checksum: 50c92dca8750c3a6bbe16c3a8af150db786636f15b0a0eba1e1f5ed2c0ae8ce06884cc7a580991c9d5f141000616c0fc5aa537e70974b0e19ad981a5cf21886b + languageName: node + linkType: hard + "@jupyterlab/docmanager@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/docmanager@npm:4.4.7" @@ -2319,6 +2428,25 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/documentsearch@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/documentsearch@npm:4.4.9" + dependencies: + "@jupyterlab/apputils": ^4.5.9 + "@jupyterlab/translation": ^4.4.9 + "@jupyterlab/ui-components": ^4.4.9 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/polling": ^2.1.4 + "@lumino/signaling": ^2.1.4 + "@lumino/widgets": ^2.7.1 + react: ^18.2.0 + checksum: a2a8e5016300cd3c88dbd931a148a5394d0f20ab638b26dd43eb131a72b14d820fe2f955dc9f7afd01383ce0d8b4cb8cd1e1642e298c9f8044b7e2e54124b937 + languageName: node + linkType: hard + "@jupyterlab/filebrowser@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/filebrowser@npm:4.4.7" @@ -2380,6 +2508,15 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/nbformat@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/nbformat@npm:4.4.9" + dependencies: + "@lumino/coreutils": ^2.2.1 + checksum: 59c73ac19ebd8d121e85916af91fc2b42e71f24d52bb7b1ac3efe58965d279edd8050934bc1079d7986c1fc061aae007e741e6889ea9a4e2f58f56b8e9c42e83 + languageName: node + linkType: hard + "@jupyterlab/notebook@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/notebook@npm:4.4.7" @@ -2431,6 +2568,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/observables@npm:^5.4.9": + version: 5.4.9 + resolution: "@jupyterlab/observables@npm:5.4.9" + dependencies: + "@lumino/algorithm": ^2.0.3 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + checksum: ce7705d469bb1d1358c15c8797cb89cb4a5f22171c17f503cfab72f19e6c73ebeae627d8b26b0e75b68a7b749c463357ea32ee8fdb9689608d03da68d501e88d + languageName: node + linkType: hard + "@jupyterlab/outputarea@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/outputarea@npm:4.4.7" @@ -2463,6 +2613,16 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/rendermime-interfaces@npm:^3.12.9": + version: 3.12.9 + resolution: "@jupyterlab/rendermime-interfaces@npm:3.12.9" + dependencies: + "@lumino/coreutils": ^1.11.0 || ^2.2.1 + "@lumino/widgets": ^1.37.2 || ^2.7.1 + checksum: c0db30ed6ea584d59d397857bb61ef36a15b166bbdaf80eafdf1a731c5a95589663ed7c3a7de698397b16348251b22e23dc90399a41d19d4993fba98964d4dea + languageName: node + linkType: hard + "@jupyterlab/rendermime@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/rendermime@npm:4.4.7" @@ -2502,6 +2662,25 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/services@npm:^7.4.9": + version: 7.4.9 + resolution: "@jupyterlab/services@npm:7.4.9" + dependencies: + "@jupyter/ydoc": ^3.1.0 + "@jupyterlab/coreutils": ^6.4.9 + "@jupyterlab/nbformat": ^4.4.9 + "@jupyterlab/settingregistry": ^4.4.9 + "@jupyterlab/statedb": ^4.4.9 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/polling": ^2.1.4 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + ws: ^8.11.0 + checksum: b1af994a2b752ed0ea60e5a53b85da3bb8a1d702407029a2ea747877a36aed142bc1d605bdef8e8f2002dc3d1ed516c2d77945dc9906cfcb58b957b9b2f6de22 + languageName: node + linkType: hard + "@jupyterlab/settingregistry@npm:^4.4.5, @jupyterlab/settingregistry@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/settingregistry@npm:4.4.7" @@ -2521,6 +2700,25 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/settingregistry@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/settingregistry@npm:4.4.9" + dependencies: + "@jupyterlab/nbformat": ^4.4.9 + "@jupyterlab/statedb": ^4.4.9 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/signaling": ^2.1.4 + "@rjsf/utils": ^5.13.4 + ajv: ^8.12.0 + json5: ^2.2.3 + peerDependencies: + react: ">=16" + checksum: f1937b8ba0486d2ebd1a7c5573c8b1cdf42aaacdfd644f1697e30c3c6d36c66faf6218908102287571d095b4e4c5d647feb1364290213c9d3f9a3ff4c74f3c73 + languageName: node + linkType: hard + "@jupyterlab/statedb@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/statedb@npm:4.4.7" @@ -2534,6 +2732,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/statedb@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/statedb@npm:4.4.9" + dependencies: + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + checksum: ec53df2570c03f3ba7e40fb0a30eb2da441c196d6261a7f347529e211b71775fa90e9174d8d3f473a0af6bc1a4e269f3edceffe5755f7f0f0557a7a645ace8be + languageName: node + linkType: hard + "@jupyterlab/statusbar@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/statusbar@npm:4.4.7" @@ -2550,6 +2761,22 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/statusbar@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/statusbar@npm:4.4.9" + dependencies: + "@jupyterlab/ui-components": ^4.4.9 + "@lumino/algorithm": ^2.0.3 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/widgets": ^2.7.1 + react: ^18.2.0 + checksum: c74382d378990e34323ad046d79985da7885af154d6bdaaad1c8e12175058978c9b2698956bad32a9592d6d543968567af1cd1eb7412d594e8ee3279675617fc + languageName: node + linkType: hard + "@jupyterlab/testing@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/testing@npm:4.4.7" @@ -2622,6 +2849,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/translation@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/translation@npm:4.4.9" + dependencies: + "@jupyterlab/coreutils": ^6.4.9 + "@jupyterlab/rendermime-interfaces": ^3.12.9 + "@jupyterlab/services": ^7.4.9 + "@jupyterlab/statedb": ^4.4.9 + "@lumino/coreutils": ^2.2.1 + checksum: 9747def9f9d6d0cf4400e615ac837ccc759e0a43adc87a97e4fa91220215bfd5ce88f3af777f9bbc9fe07f950fb85714c7ff555f2df02dc8aa30a2dcda71e3c1 + languageName: node + linkType: hard + "@jupyterlab/ui-components@npm:^4.4.7": version: 4.4.7 resolution: "@jupyterlab/ui-components@npm:4.4.7" @@ -2653,6 +2893,37 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/ui-components@npm:^4.4.9": + version: 4.4.9 + resolution: "@jupyterlab/ui-components@npm:4.4.9" + dependencies: + "@jupyter/react-components": ^0.16.6 + "@jupyter/web-components": ^0.16.6 + "@jupyterlab/coreutils": ^6.4.9 + "@jupyterlab/observables": ^5.4.9 + "@jupyterlab/rendermime-interfaces": ^3.12.9 + "@jupyterlab/translation": ^4.4.9 + "@lumino/algorithm": ^2.0.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/polling": ^2.1.4 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/virtualdom": ^2.0.3 + "@lumino/widgets": ^2.7.1 + "@rjsf/core": ^5.13.4 + "@rjsf/utils": ^5.13.4 + react: ^18.2.0 + react-dom: ^18.2.0 + typestyle: ^2.0.4 + peerDependencies: + react: ^18.2.0 + checksum: 84c3e71b27a4a8031963c1e0f27b2409b781a1bc2ade569fa08e05a1263172ef7cd2664fe8e670ce3f704e2bec7a56fd21ecff1b6616062d34ddd632f049d606 + languageName: node + linkType: hard + "@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0, @lezer/common@npm:^1.2.1": version: 1.2.3 resolution: "@lezer/common@npm:1.2.3" @@ -6794,10 +7065,14 @@ __metadata: version: 0.0.0-use.local resolution: "jupyterlab-dyno@workspace:." dependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.2.0 "@jupyterlab/application": ^4.4.5 "@jupyterlab/builder": ^4.4.5 + "@jupyterlab/codemirror": ^4.4.5 "@jupyterlab/settingregistry": ^4.4.5 "@jupyterlab/testutils": ^4.4.5 + "@lezer/highlight": ^1.0.0 "@types/jest": ^29.5.14 "@types/json-schema": ^7.0.15 "@types/react": ^18.3.23