From 597cb157b2ea3a884a183232ac18913b3fc29c93 Mon Sep 17 00:00:00 2001 From: Emil Valeev Date: Fri, 17 Nov 2023 01:47:20 +0600 Subject: [PATCH] wip(arch): mermaid --- ARCHITECTURE.md | 42 ++++++++++++ README.md | 2 + cmd/lsp/general_messages.go | 7 ++ web/ARCHITECTURE.md | 15 +++++ web/package.json | 17 +++-- web/src/editor/index.ts | 2 +- web/src/extension.ts | 66 +++++++++++++++++-- .../{editor/webview_helpers.ts => webview.ts} | 0 web/webview/src/App.tsx | 16 ++++- web/webview/src/components/imports_view.tsx | 40 +++++------ web/webview/src/hooks/use_file_state.ts | 22 +++---- web/webview/src/index.css | 2 +- 12 files changed, 178 insertions(+), 53 deletions(-) create mode 100644 web/ARCHITECTURE.md rename web/src/{editor/webview_helpers.ts => webview.ts} (100%) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e69de29b..e25011c1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -0,0 +1,42 @@ +# ARCHITECTURE + +## Production + +```mermaid +flowchart LR + builder-->|raw-program|compiler + subgraph builder + git-client + end + subgraph compiler + parser-->|program|analyzer + subgraph analyzer + typesystem + end + analyzer-->|analyzed-program|irgen + irgen-->|ir|decoder + end + compiler-->|protobuf|VM + subgraph VM + loader-->|ir|runtime + runtime + subgraph runtime + connector + func-runner + end + end +``` + +## Development + +```mermaid +flowchart LR + subgraph interpreter + builder-->compiler + compiler-->runtime + end +``` + +## VSCode Extension + +See [web/ARCHITECTURE.md](./web/ARCHITECTURE.md) \ No newline at end of file diff --git a/README.md b/README.md index a4c60181..bcd1c712 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,5 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) ## FAQ See [FAQ](./docs/faq.md) + + diff --git a/cmd/lsp/general_messages.go b/cmd/lsp/general_messages.go index 8d343148..6e687da6 100644 --- a/cmd/lsp/general_messages.go +++ b/cmd/lsp/general_messages.go @@ -2,12 +2,15 @@ package main import ( "context" + "fmt" "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) func (s Server) Initialize(glspCtx *glsp.Context, params *protocol.InitializeParams) (any, error) { + fmt.Println("===Initialize===") + result := protocol.InitializeResult{ Capabilities: s.handler.CreateServerCapabilities(), ServerInfo: &protocol.InitializeResultServerInfo{ @@ -36,10 +39,14 @@ func (s Server) Initialize(glspCtx *glsp.Context, params *protocol.InitializePar return result, nil } +// Initialized is called when vscode-extension is initialized. +// It spawns goroutines for sending indexing messages and warnings func (srv Server) Initialized(glspCtx *glsp.Context, params *protocol.InitializedParams) error { go func() { for prog := range srv.mod { + fmt.Println("new message from mod chan") glspCtx.Notify("neva/workdir_indexed", prog) + fmt.Println("message sent to vscode") } }() go func() { diff --git a/web/ARCHITECTURE.md b/web/ARCHITECTURE.md new file mode 100644 index 00000000..2a70b256 --- /dev/null +++ b/web/ARCHITECTURE.md @@ -0,0 +1,15 @@ +# ARCHITECTURE + +```mermaid +flowchart LR + language-server-->vscode + subgraph language-server + indexer + end + subgraph vscode + webview-->extension + extension-->webview + end + webview-->vscode + vscode-->language-server +``` diff --git a/web/package.json b/web/package.json index d97e9de9..6db5f904 100644 --- a/web/package.json +++ b/web/package.json @@ -74,16 +74,15 @@ "path": "./syntaxes/neva.tmLanguage.json" } ], - "customEditors": [ + "commands": [ { - "viewType": "neva.editNeva", - "displayName": "Neva", - "selector": [ - { - "filenamePattern": "*.neva" - } - ], - "priority": "default" + "command": "neva.openPreview", + "title": "Neva", + "category": "Neva", + "icon": { + "light": "assets/logo.png", + "dark": "assets/logo.png" + } } ] }, diff --git a/web/src/editor/index.ts b/web/src/editor/index.ts index 84ae01cd..38a87073 100644 --- a/web/src/editor/index.ts +++ b/web/src/editor/index.ts @@ -8,7 +8,7 @@ import { ExtensionContext, } from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; -import { getWebviewContent, sendMsgToWebview } from "./webview_helpers"; +import { getWebviewContent, sendMsgToWebview } from "../webview"; export class NevaEditor implements CustomTextEditorProvider { private readonly context: ExtensionContext; diff --git a/web/src/extension.ts b/web/src/extension.ts index 29cd3f84..557e69db 100644 --- a/web/src/extension.ts +++ b/web/src/extension.ts @@ -1,7 +1,15 @@ -import { ExtensionContext, window } from "vscode"; +import { + ExtensionContext, + window, + commands, + ViewColumn, + Uri, + WebviewPanel, +} from "vscode"; import { LanguageClient } from "vscode-languageclient/node"; import { NevaEditor } from "./editor"; import { setupLsp } from "./lsp"; +import { getWebviewContent, sendMsgToWebview } from "./webview"; let lspClient: LanguageClient; const viewType = "neva.editNeva"; @@ -9,18 +17,64 @@ const viewType = "neva.editNeva"; export async function activate(context: ExtensionContext) { console.log("vscode-neva: activated"); - // Language Server + // Run language server, initialize client and establish connection lspClient = setupLsp(context); lspClient.start(); lspClient.onNotification("neva/analyzer_message", (message: string) => { window.showWarningMessage(message); }); - // Custom Editor - const editor = new NevaEditor(context, lspClient); + // Track the current panel with a webview + let currentPanel: WebviewPanel | undefined = undefined; + + // Register preview command context.subscriptions.push( - window.registerCustomEditorProvider(viewType, editor, { - supportsMultipleEditorsPerDocument: true, + commands.registerCommand("neva.openPreview", () => { + const columnToShowIn = window.activeTextEditor + ? window.activeTextEditor.viewColumn + : undefined; + + // If we already have a panel, show it in the target column + if (currentPanel) { + currentPanel.reveal(columnToShowIn); + } else { + // Otherwise, create a new panel + currentPanel = window.createWebviewPanel( + "neva", + "Neva: Preview", + ViewColumn.Beside, + { + enableScripts: true, + localResourceRoots: [ + (Uri as any).joinPath(context.extensionUri, "out"), + (Uri as any).joinPath(context.extensionUri, "webview/dist"), + ], + } + ); + + // Set content + currentPanel.webview.html = getWebviewContent( + currentPanel.webview, + context.extensionUri + ); + + lspClient.onNotification("neva/workdir_indexed", (indexedModule) => { + sendMsgToWebview( + currentPanel!, + window.activeTextEditor?.document!, + indexedModule + ); + }); + + // Reset when the current panel is closed + currentPanel.onDidDispose( + () => { + currentPanel = undefined; + }, + null, + context.subscriptions + ); + } }) ); } diff --git a/web/src/editor/webview_helpers.ts b/web/src/webview.ts similarity index 100% rename from web/src/editor/webview_helpers.ts rename to web/src/webview.ts diff --git a/web/webview/src/App.tsx b/web/webview/src/App.tsx index da8fa546..d5262b50 100644 --- a/web/webview/src/App.tsx +++ b/web/webview/src/App.tsx @@ -1,4 +1,8 @@ -import { VSCodeDivider } from "@vscode/webview-ui-toolkit/react"; +import { + VSCodeDivider, + VSCodePanelTab, + VSCodePanels, +} from "@vscode/webview-ui-toolkit/react"; import { ImportsView, TypesView, @@ -14,6 +18,14 @@ export default function App() { return (
+ + USE + TYPES + CONST + INTERFACES + COMPONENTS + + @@ -25,7 +37,7 @@ export default function App() { -

Constants

+

Const

diff --git a/web/webview/src/components/imports_view.tsx b/web/webview/src/components/imports_view.tsx index 5c44de07..0d851192 100644 --- a/web/webview/src/components/imports_view.tsx +++ b/web/webview/src/components/imports_view.tsx @@ -1,31 +1,25 @@ -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; +import { + VSCodeDataGrid, + VSCodeDataGridCell, + VSCodeDataGridRow, +} from "@vscode/webview-ui-toolkit/react"; export function ImportsView(props: { imports: Array<{ alias: string; path: string }>; style?: object; }) { return ( -
-

Use

- {props.imports.map((entry, idx, imports) => { - const { path } = entry; - return ( -
- - console.log("navigation not implemented")} - /> - -
- ); - })} -
+ + {props.imports.map((importDef) => ( + + + {importDef.alias} + + + {importDef.path} + + + ))} + ); } diff --git a/web/webview/src/hooks/use_file_state.ts b/web/webview/src/hooks/use_file_state.ts index e8b6e74d..dd411e28 100644 --- a/web/webview/src/hooks/use_file_state.ts +++ b/web/webview/src/hooks/use_file_state.ts @@ -31,34 +31,34 @@ interface GroupedEntities { const vscodeApi = acquireVsCodeApi(); // File state is grouped and sorted render-friendly object -interface FileState { +interface RenderFriendly { imports: Array<{ alias: string; path: string }>; entities: GroupedEntities; } // UseFileState returns state that is easy to render. It also does memorization to avoid re-rendering -export function useFileState(): FileState { +export function useFileState(): RenderFriendly { const persistedState = vscodeApi.getState(); // load persistent state - console.log("persistent state", persistedState); + + // copy persistent state from disk it to memory const [state, setState] = useState( persistedState - ); // copy it to memory + ); - // subscribe to state updates from vscode + // subscribe to updates from vscode extension useEffect(() => { const listener = (event: { data: VSCodeMessageData }) => { console.log("event from vscode", { event }); - setState(event.data); // update both local state - vscodeApi.setState(event.data); // and persistent state to use when tab is reopened + setState(event.data); // update both memory + vscodeApi.setState(event.data); // and persistent state (to reuse when tab is reopened) }; window.addEventListener("message", listener); return () => window.removeEventListener("message", listener); }, []); - // given program state find file corresponding to current opened document and transform it - // to a render-friendly form, memoize the result - const fileState: FileState = useMemo(() => { - const result: FileState = { + // use state to compute render-friendly state and memoize the result + const fileState: RenderFriendly = useMemo(() => { + const result: RenderFriendly = { imports: [], entities: { types: [], interfaces: [], constants: [], components: [] }, }; diff --git a/web/webview/src/index.css b/web/webview/src/index.css index 2aea9050..170f6b29 100644 --- a/web/webview/src/index.css +++ b/web/webview/src/index.css @@ -12,7 +12,7 @@ } body { - font-family: 'Bai Jamjuree', sans-serif; + /* font-family: 'Bai Jamjuree', sans-serif; */ background-color: var(--background); padding: 20px; }