{"id":19788862,"url":"https://github.com/niklaskorz/puredit","last_synced_at":"2026-03-10T11:31:23.098Z","repository":{"id":41488598,"uuid":"430712148","full_name":"niklaskorz/puredit","owner":"niklaskorz","description":"A projectional editor that uses textual code as its source of truth.","archived":false,"fork":false,"pushed_at":"2024-07-26T16:28:41.000Z","size":6301,"stargazers_count":26,"open_issues_count":0,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-03T02:13:18.012Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://puredit.korz.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/niklaskorz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-11-22T13:12:45.000Z","updated_at":"2024-09-08T20:52:03.000Z","dependencies_parsed_at":"2023-10-13T03:49:08.608Z","dependency_job_id":"4e496b47-7c78-4356-a9b0-3a4edd419126","html_url":"https://github.com/niklaskorz/puredit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/niklaskorz/puredit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklaskorz%2Fpuredit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklaskorz%2Fpuredit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklaskorz%2Fpuredit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklaskorz%2Fpuredit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/niklaskorz","download_url":"https://codeload.github.com/niklaskorz/puredit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/niklaskorz%2Fpuredit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30332262,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T05:25:20.737Z","status":"ssl_error","status_checked_at":"2026-03-10T05:25:17.430Z","response_time":106,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T06:29:03.301Z","updated_at":"2026-03-10T11:31:23.053Z","avatar_url":"https://github.com/niklaskorz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Project Overview\nThis repository contains the accompanying source code and examples of the following paper:\n\n[Niklas Korz](https://2023.splashcon.org/profile/niklaskorz), [Artur Andrzejak](https://aip.ifi.uni-heidelberg.de/team/aa)\n\n**Virtual Domain Specific Languages via Embedded Projectional Editing**\n\nPublished at 22nd International Conference on Generative Programming: Concepts \u0026 Experiences ([GPCE 2023](https://2023.splashcon.org/program/program-splash-2023/)), in conjunction with ACM SPLASH 2023, 22-27 October 2023, Cascais, Portugal.\n\n\n### Summary\nWe propose here an approach which represents a subset of a General-Purpose Programming Language (GPL) as GUI widgets in a hybrid editor. It relies on matching parametrized patterns against the GPL program, and displaying the matched parts as dynamically rendered widgets. Such widgets can be interpreted as components of an external DSL. Since the source code is serialized as GPL text without annotations, there is no DSL outside the editor - hence the term ‘virtual’ DSL.\n\nThe underlying GPL and the virtual DSL can be mixed in a compositional way, with zero cost of their integration. The project infrastructure does not need to be adapted. Furthermore, our approach works with mainstream GPLs like Python or JavaScript.\n\nTo lower the development effort of such virtual DSLs, we also propose an approach to generate patterns and the corresponding text-only GUI widgets from pairs of examples.\n\nA live demo of the system can be accessed [here](https://puredit.korz.dev/).\n\n### Why the name Puredit?\n\n\u003e Purism, referring to the arts, was a movement (...) where objects are represented as elementary forms devoid of detail. ([Wikipedia](https://en.wikipedia.org/wiki/Purism))\n\nThe **Pur**ist **edit**or is a projectional editor that uses textual code as its source of truth. Unlike other projectional editors, Puredit is based on the assumption that parsers are fast enough to continuously react to changes to a document and update the projections accordingly. Projections are derived from patterns on the abstract syntax tree. To make the definition of patterns easy, they are parsed from actual code snippets in the target language.\n\n## Project structure\n\n- `apps/`: executable applications and demos\n  - `example/`: an example of a projectional data manipulation DSL based on the TypeScript programming language, which is also available at \u003chttps://puredit.korz.dev/\u003e\n  - `generate/`: an application for generating code pattern templates and projection components from samples\n  - `jupyter/`: an example of a projectional DSL for data analysis of Excel sheets, based on Python and integrated into [JupyterLab](https://jupyter.org/)\n  - `parser-playground/`: an experimentation playground used for debugging our pattern matching algorithm\n  - `python-dsl/`: an example of a projectional data manipulation DSL based on the Python programming language\n- `packages/`: reusable packages that together are used to create a projectional editor\n  - `codemirror-typescript/`: a client-side language server for the TypeScript programming language integrated into [CodeMirror 6](https://codemirror.net/), based on [prisma/text-editors](https://github.com/prisma/text-editors)\n  - `parser/`: a library for defining syntax tree patterns through code templates and finding nodes matching these patterns in a program's syntax tree\n  - `projections/`: an extension for the [CodeMirror 6](https://codemirror.net/) editor for detecting syntax tree patterns in a document and replacing them with interactive projection widgets implemented as Svelte components\n  - `simple-projection/`: an alternative for the Svelte-based implementation components, limited to simple linear texts widgets with string fields\n\n## Installation and development\n\nThis projects requires [Node](https://nodejs.org/en/) 16 LTS and [npm](https://www.npmjs.com/) 8.\nNode 18 may work but has not been tested.\nThen, install the dependencies using `npm install`.\n\n### Develop\n\nTo develop all apps and packages, run the following command:\n\n```\nnpm run dev\n```\n\nThis will start development servers for `apps/example`, `apps/parser-playground` and `apps/python-dsl`.\nFor the JupyterLab example, see the next section.\n\n### Build\n\nTo build all apps and packages, run the following command:\n\n```\nnpm run build\n```\n\n### Linting and type checking\n\nTo lint and typecheck all apps and packages, run the following command:\n\n```\nnpm run lint\n```\n\n## TypeScript DSL example\n\nTo build and run the TypeScript DSL example, run the following commands:\n\n```sh\nnpm install\nnpm -w apps/example run dev\n```\n\nAlternatively, the TypeScript DSL example can be built and run using [Docker](https://docs.docker.com/):\n\n```sh\ndocker build -t puredit-example -f apps/example/Dockerfile .\ndocker run -p 3000:80 puredit-example\n```\n\nAfterwards, the example can be accessed in a browser on \u003chttp://localhost:3000\u003e.\n\n## JupyterLab example\n\nTo build and run the JupyterLab DSL example, run the following commands:\n\n### Virtual environment\n\n```sh\n# Create and activate Python virtual environment\npython3 -m venv .venv\nsource .venv/bin/activate\n\n# Install dependencies and our Jupyter extension\nnpm install\npip install -r apps/jupyter/requirements.txt\nnpm -w apps/jupyter run build:prod\npip install apps/jupyter\n\n# Start JupyterLab\ncd apps/jupyter/example\njupyter lab\n```\n\n### Docker\n\nAlternatively, the JupyterLab example can be built and run using [Docker](https://docs.docker.com/):\n\n```sh\ndocker build -t puredit-jupyter -f apps/jupyter/Dockerfile .\ndocker run -p 8888:8888 puredit-jupyter\n```\n\nAfterwards, JupyterLab can be accessed in a browser on \u003chttp://localhost:8888\u003e.\n\n## Creating new projections\n\nTo create new projection components and their code pattern templates, you can either start from scratch or use the output of our sample-based generator as foundation.\n\n### From scratch\n\nTo create a new projection from scratch, you need to implement two things: an entrypoint for your projection that defines the pattern and the projection, as well as a Svelte component providing the implementation for your projection widget.\nA good starting point is to copy and adapt one of the projections from `apps/example/src/projections`.\nUse the `arg`, `block`, and `contextVariable` template helpers from the `@puredit/parser` inside your code templates to create dynamic patterns.\n\n### Using the generator\n\nThe sample-based generator takes a text files containing both code and projection samples as input.\nAll samples are divided by an empty line.\nThe code samples are divided from the projection samples by a line containg only `---` as content.\nYou can look at the datasets in `apps/generate/examples` as reference.\nExecute the generator as follows, replacing the argument placeholders with your according values:\n\n```sh\nnpm start -w apps/generate -- \\\n    --language \u003clanguage\u003e \\\n    --parser-name \u003cparser-name\u003e \\\n    --parser-module \u003cparser-module\u003e \\\n    --projection-name \u003cprojection-name\u003e \\\n    --samples \u003csamples\u003e \\\n    --target-dir \u003ctarget-dir\u003e\n```\n\nNote that the path of `parser-module` must be relative to the path of `target-dir`.\nFor example, to generate the pattern and projections files `removeProjection.ts` and `RemoveProjection.svelte` for `apps/generate/examples/ts-remove.txt` with target app `apps/examples`:\n\n```sh\nnpm start -w apps/generate -- \\\n    --language ts \\\n    --parser-name tsParser \\\n    --parser-module './parser' \\\n    --projection-name remove \\\n    --samples apps/generate/examples/ts-remove.txt \\\n    --target-dir apps/example/src/projections\n```\n\nOn Windows Powershell, `\\` does not work for multiline commands, so write everything in one line instead:\n\n```sh\nnpm start -w apps/generate -- --language ts --parser-name tsParser --parser-module \"./parser\" --projection-name remove --samples apps/generate/examples/ts-remove.txt --target-dir apps/example/src/projections\n```\n\nSimilarly, to generate the pattern and projections files `takeProjection.ts` and `TakeProjection.svelte` for `apps/generate/examples/py-take.txt` with target app `apps/jupyter`:\n\n```sh\nnpm start -w apps/generate -- \\\n    --language py \\\n    --parser-name pythonParser \\\n    --parser-module './parser' \\\n    --projection-name take \\\n    --samples apps/generate/examples/py-take.txt \\\n    --target-dir apps/jupyter/src/projections\n```\n\nAfterwards, register the new projection by adding them to the lists in `apps/example/src/projections/index.ts` or respectively `apps/jupyter/src/projections/index.ts`.\n\n## Integrating the projectional editor\n\nTo integrate the projectional editor into your own project, see `apps/example`, in particular `apps/example/src/Editor.svelte`.\nThere, a new CodeMirror editor is created and mounted that is making use of our projectional editing extension.\nFor a more specialized example, see `apps/jupyter`.\nIn `apps/jupyter/src/editor.ts`, a wrapper for CodeMirror 6 is provided that makes it compatible to JupyterLab's own editor.\nOn top of this, `apps/jupyter/src/index.ts` provides a Jupyter [frontend extension](https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html) that replaces Jupyter's own editor with our projectional editor.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fniklaskorz%2Fpuredit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fniklaskorz%2Fpuredit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fniklaskorz%2Fpuredit/lists"}