diff --git a/examples/pyright/.gitignore b/examples/pyright/.gitignore
new file mode 100644
index 0000000..f694356
--- /dev/null
+++ b/examples/pyright/.gitignore
@@ -0,0 +1,26 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+bin/
diff --git a/examples/pyright/README.md b/examples/pyright/README.md
new file mode 100644
index 0000000..2bc22b9
--- /dev/null
+++ b/examples/pyright/README.md
@@ -0,0 +1,20 @@
+# @qualified/codemirror-workspace Python demo
+
+---
+
+Install [`pnpm`] if you don't have it installed:
+
+```
+npm i -g pnpm@6
+```
+
+Install [`lsp-ws-proxy`](https://github.com/qualified/lsp-ws-proxy) (v0.8.0+ is required) by downloading a binary from [releases](https://github.com/qualified/lsp-ws-proxy/releases) and moving it in `PATH` or `./bin`.
+
+Run the following to start a dev server:
+
+```bash
+pnpm install
+pnpm dev
+```
+
+[`pnpm`]: https://pnpm.js.org/
diff --git a/examples/pyright/favicon.svg b/examples/pyright/favicon.svg
new file mode 100644
index 0000000..de4aedd
--- /dev/null
+++ b/examples/pyright/favicon.svg
@@ -0,0 +1,15 @@
+
diff --git a/examples/pyright/index.html b/examples/pyright/index.html
new file mode 100644
index 0000000..da31f23
--- /dev/null
+++ b/examples/pyright/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ CodeMirror Workspace Python Demo
+
+
+
+
+
+
+
diff --git a/examples/pyright/package.json b/examples/pyright/package.json
new file mode 100644
index 0000000..e0f1300
--- /dev/null
+++ b/examples/pyright/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@qualified/codemirror-workspace-demo-python",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "dev": "concurrently 'vite' 'pnpm start-proxy'",
+ "build": "tsc && vite build",
+ "preview": "concurrently 'vite preview' 'pnpm start-proxy'",
+ "start-proxy": "cd workspace && PATH=$(pwd)/../bin:$PATH RUST_LOG=info,lsp_ws_proxy=debug lsp-ws-proxy --remap -l 9990 -- pyright-langserver --stdio"
+ },
+ "dependencies": {
+ "@qualified/codemirror-workspace": "workspace:0.5.0",
+ "codemirror": "^5.59.2",
+ "marked": "^4.0.17"
+ },
+ "devDependencies": {
+ "@types/codemirror": "^5.0.0",
+ "@types/marked": "^4.0.3",
+ "concurrently": "^7.2.2",
+ "pyright": "^1.1.271",
+ "typescript": "^4.7.4",
+ "vite": "^2.9.9"
+ }
+}
diff --git a/examples/pyright/src/main.ts b/examples/pyright/src/main.ts
new file mode 100644
index 0000000..039b3fd
--- /dev/null
+++ b/examples/pyright/src/main.ts
@@ -0,0 +1,122 @@
+import "./style.css";
+
+import CodeMirror from "codemirror";
+import "codemirror/mode/python/python";
+import "codemirror/lib/codemirror.css";
+import "codemirror/theme/idea.css";
+// ShowHint addon is required for completion capability.
+import "codemirror/addon/hint/show-hint.css";
+import "codemirror/addon/hint/show-hint";
+import "codemirror/addon/edit/matchbrackets";
+import "codemirror/addon/edit/closebrackets";
+import "codemirror/addon/runmode/runmode";
+// import "codemirror/keymap/vim";
+
+import { marked } from "marked";
+
+import { Workspace } from "@qualified/codemirror-workspace";
+import "@qualified/codemirror-workspace/css/default.css";
+
+import preloadedPy from "../workspace/preloaded.py?raw";
+import solutionPy from "../workspace/solution.py?raw";
+import solutionTestPy from "../workspace/tests/test_solution.py?raw";
+
+const modeMap: { [k: string]: string } = {
+ python: "python",
+};
+
+const highlight = (code: string, language: string) => {
+ const mode = modeMap[language] || "text/plain";
+ const tmp = document.createElement("div");
+ CodeMirror.runMode(code, mode, tmp, { tabSize: 4 });
+ return tmp.innerHTML;
+};
+
+marked.use({
+ // @ts-ignore renderer can be object literal
+ renderer: {
+ code(code: string, language: string | undefined) {
+ if (!language) language = "text";
+ code = highlight(code, language);
+ // We need to add a class for the theme (e.g., `cm-s-idea`) on the wrapper.
+ // If we're using a custom theme, it can apply its styles to `code[class^="language-"]`
+ // and use Marked's default `code` with `highlight` option.
+ return `${code}
`;
+ },
+ },
+});
+
+const $ = (sel: string) => {
+ const el = document.querySelector(sel);
+ if (!el) throw new Error(`No element matching ${sel}`);
+ return el as HTMLElement;
+};
+
+const config: CodeMirror.EditorConfiguration = {
+ theme: "idea",
+ // keyMap: "vim",
+ gutters: ["cmw-gutter"],
+ lineNumbers: true,
+ matchBrackets: true,
+ autoCloseBrackets: true,
+ indentUnit: 4,
+};
+
+const workspace = new Workspace({
+ rootUri: "source://",
+ getLanguageAssociation: (uri: string) => {
+ if (uri.endsWith(".py")) {
+ return {
+ languageId: "python",
+ languageServerIds: ["pyright-langserver"],
+ };
+ }
+ // Workspace will ignore the file if null is returned.
+ return null;
+ },
+ getConnectionString: async (id: string) => {
+ return id ? `ws://localhost:9990?name=${id}` : "";
+ },
+ // Support Markdown documentation
+ renderMarkdown: (markdown) => marked(markdown),
+ configs: {
+ "pyright-langserver": {
+ settings: {
+ python: {
+ analysis: {
+ autoSearchPaths: true,
+ useLibraryCodeForTypes: true,
+ diagnosticMode: "openFiles",
+ },
+ },
+ },
+ },
+ },
+});
+
+workspace.openTextDocument(
+ "preloaded.py",
+ CodeMirror($("#preloaded-editor"), {
+ ...config,
+ mode: "python",
+ value: preloadedPy,
+ })
+);
+
+workspace.openTextDocument(
+ "solution.py",
+ CodeMirror($("#solution-editor"), {
+ ...config,
+ mode: "python",
+ value: solutionPy,
+ })
+);
+
+workspace.openTextDocument(
+ "tests/solution_test.py",
+ CodeMirror($("#test-editor"), {
+ ...config,
+ mode: "python",
+ value: solutionTestPy,
+ })
+);
diff --git a/examples/pyright/src/style.css b/examples/pyright/src/style.css
new file mode 100644
index 0000000..d14f887
--- /dev/null
+++ b/examples/pyright/src/style.css
@@ -0,0 +1,21 @@
+.CodeMirror {
+ height: 100% !important;
+}
+
+.editor {
+ width: 100%;
+ height: 220px;
+ overflow: auto;
+ border-bottom: 1px solid #ccc;
+}
+
+.editors {
+ padding: 2rem;
+ margin: 0 auto;
+ max-width: 80ch;
+}
+
+.buttons {
+ margin: 0 auto;
+ text-align: center;
+}
diff --git a/examples/pyright/src/vite-env.d.ts b/examples/pyright/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/examples/pyright/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/pyright/tsconfig.json b/examples/pyright/tsconfig.json
new file mode 100644
index 0000000..fbd0225
--- /dev/null
+++ b/examples/pyright/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ESNext", "DOM"],
+ "moduleResolution": "Node",
+ "strict": true,
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "skipLibCheck": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/pyright/workspace/preloaded.py b/examples/pyright/workspace/preloaded.py
new file mode 100644
index 0000000..b134e54
--- /dev/null
+++ b/examples/pyright/workspace/preloaded.py
@@ -0,0 +1,4 @@
+# preloaded.py
+def helper(a, b):
+ """ A helper doc. """
+ return a * b
diff --git a/examples/pyright/workspace/pyrightconfig.json b/examples/pyright/workspace/pyrightconfig.json
new file mode 100644
index 0000000..69deaf0
--- /dev/null
+++ b/examples/pyright/workspace/pyrightconfig.json
@@ -0,0 +1,8 @@
+{
+ "include": ["./"],
+ "executionEnvironments": [
+ {
+ "root": "./"
+ }
+ ]
+}
diff --git a/examples/pyright/workspace/solution.py b/examples/pyright/workspace/solution.py
new file mode 100644
index 0000000..03bf3f8
--- /dev/null
+++ b/examples/pyright/workspace/solution.py
@@ -0,0 +1,7 @@
+# solution.py
+from preloaded import helper
+
+
+def add(a, b):
+ print(helper(a, b))
+ return a + b
diff --git a/examples/pyright/workspace/tests/test_solution.py b/examples/pyright/workspace/tests/test_solution.py
new file mode 100644
index 0000000..57ed4ab
--- /dev/null
+++ b/examples/pyright/workspace/tests/test_solution.py
@@ -0,0 +1,10 @@
+# tests/test_solution.py
+import unittest
+
+# TODO Fix `solution` not resolved
+from solution import add
+
+
+class TestAddition(unittest.TestCase):
+ def test_add(self):
+ self.assertEqual(add(1, 1), 2)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0f68fed..4db882b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,6 +43,29 @@ importers:
vscode-css-languageserver-bin: 1.4.0
vscode-html-languageserver-bin: 1.4.0
+ examples/pyright:
+ specifiers:
+ '@qualified/codemirror-workspace': workspace:0.5.0
+ '@types/codemirror': ^5.0.0
+ '@types/marked': ^4.0.3
+ codemirror: ^5.59.2
+ concurrently: ^7.2.2
+ marked: ^4.0.17
+ pyright: ^1.1.271
+ typescript: ^4.7.4
+ vite: ^2.9.9
+ dependencies:
+ '@qualified/codemirror-workspace': link:../../packages/codemirror-workspace
+ codemirror: 5.63.3
+ marked: 4.0.17
+ devDependencies:
+ '@types/codemirror': 5.60.0
+ '@types/marked': 4.0.3
+ concurrently: 7.2.2
+ pyright: 1.1.271
+ typescript: 4.7.4
+ vite: 2.9.12
+
examples/rust-analyzer:
specifiers:
'@qualified/codemirror-workspace': workspace:0.5.0
@@ -4549,6 +4572,12 @@ packages:
resolution: {integrity: sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ==}
dev: true
+ /pyright/1.1.271:
+ resolution: {integrity: sha512-46QQLQLT5U+wmKqG9R193loBASqqcIZYwWrwTCWNTHIRYTxEbaHwGWDlmX19R3lmlIBDu9I9m5MyPmYs/2v5dg==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+ dev: true
+
/queue-microtask/1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true