Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute yjs from insight script #11965

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/ydoc-server-polyglot/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const globals = {
}

const ctx = await esbuild.context({
outfile: 'dist/main.cjs',
outdir: 'dist',
sourcemap: 'linked',
entryPoints: ['src/main.ts'],
entryPoints: ['src/main.ts', 'src/insight.ts'],
bundle: true,
platform: 'browser',
define: {
Expand Down
6 changes: 4 additions & 2 deletions app/ydoc-server-polyglot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
"main": "./dist/main.js",
"scripts": {
"compile": "node ./build.mjs build",
"start": "node ./dist/main.cjs",
"start": "node ./dist/main.js",
"dev:watch": "node ./build.mjs watch",
"lint": "eslint . --cache --max-warnings=0"
},
"dependencies": {
"y-websocket": "^1.5.0",
"ydoc-server": "workspace:*",
"ydoc-shared": "workspace:*"
"ydoc-shared": "workspace:*",
"yjs": "^13.6.7"
},
"devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
Expand Down
127 changes: 127 additions & 0 deletions app/ydoc-server-polyglot/src/insight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { deserializeIdMap } from 'ydoc-server'
import * as Ast from 'ydoc-shared/ast'
import { splitFileContents } from 'ydoc-shared/ensoFile'
import { DistributedProject, ModuleDoc } from 'ydoc-shared/yjsModel'

Check warning on line 4 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L4

[@typescript-eslint/no-unused-vars] 'ModuleDoc' is defined but never used. Allowed unused vars must match /^_/u.

Check warning on line 4 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L4

[@typescript-eslint/no-unused-vars] 'ModuleDoc' is defined but never used. Allowed unused vars must match /^_/u.
import { WebsocketProvider } from 'y-websocket'
import * as Y from 'yjs'

interface SubdocsEvent {
loaded: Set<Y.Doc>
added: Set<Y.Doc>
removed: Set<Y.Doc>
}

print("Initializing Insight: " + JSON.stringify(insight));

const PROJECT_NAME = 'NewProject1'
const RETURN_VALUE_KEY = 'insight_return_value'

//const doc = new ModuleDoc(new Y.Doc())
const doc = new Y.Doc()
const syncModule = new Ast.MutableModule(doc)
let spanMap = null

function attachProvider(url: string, room: string, doc: Y.Doc) {

Check warning on line 24 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L24

[@typescript-eslint/no-unused-vars] 'attachProvider' is defined but only used as a type. Allowed unused vars must match /^_/u.

Check warning on line 24 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L24

[@typescript-eslint/no-unused-vars] 'attachProvider' is defined but only used as a type. Allowed unused vars must match /^_/u.
print(`attachProvider ${url} ${room}`)
const provider = new WebsocketProvider(url, room, doc)
const onSync = () => doc.emit('sync', [true, doc])
const onDrop = () => doc.emit('sync', [false, doc])

const attachedSubdocs = new Map<Y.Doc, ReturnType<typeof attachProvider>>()

function onSubdocs(e: SubdocsEvent) {
e.loaded.forEach((subdoc) => {
attachedSubdocs.set(subdoc, attachProvider(url, subdoc.guid, subdoc))
})
e.removed.forEach((subdoc) => {
const subdocProvider = attachedSubdocs.get(subdoc)
attachedSubdocs.delete(subdoc)
if (subdocProvider != null) {
subdocProvider.dispose()
}
})
}

provider.on('sync', onSync)
provider.on('connection-close', onDrop)
provider.on('connection-error', onDrop)
doc.on('subdocs', onSubdocs)

function dispose() {
provider.disconnect()
provider.off('sync', onSync)
provider.off('connection-close', onDrop)
provider.off('connection-error', onDrop)
doc.off('subdocs', onSubdocs)
attachedSubdocs.forEach((subdocProvider) => {
subdocProvider.dispose()
})
}
return { provider, dispose }
}

const d = new Y.Doc()
const project = new DistributedProject(d)
//const provider = attachProvider('ws://[::1]:5976/project', 'index', d)

function parseContents(contents: string, syncModule: Ast.MutableModule) {
const { code, idMapJson, metadataJson } = splitFileContents(contents)

Check warning on line 68 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L68

[@typescript-eslint/no-unused-vars] 'metadataJson' is assigned a value but never used. Allowed unused vars must match /^_/u.

Check warning on line 68 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L68

[@typescript-eslint/no-unused-vars] 'metadataJson' is assigned a value but never used. Allowed unused vars must match /^_/u.
const parsedIdMap = deserializeIdMap(idMapJson)

const { root, spans } = Ast.parseModuleWithSpans(code, syncModule)
syncModule.setRoot(root)
spanMap = spans

Ast.setExternalIds(syncModule, spans, parsedIdMap)
print(`[parseContents] idMap.entries=${parsedIdMap.entries()}`)
}

insight.on("source", function(ctx, frame) {

Check warning on line 79 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L79

[@typescript-eslint/no-unused-vars] 'frame' is defined but never used. Allowed unused args must match /^_/u.

Check warning on line 79 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L79

[@typescript-eslint/no-unused-vars] 'frame' is defined but never used. Allowed unused args must match /^_/u.
print(`[source] ctx.uri=${ctx.name}`)
if (ctx.name.includes(PROJECT_NAME)) {
print(`[source] MATCHED ctx=${JSON.stringify(ctx)}`)
parseContents(ctx.characters, syncModule)
}
})

function resultToString(result: any): string {
return '' + result;
}

/*
class IdMap {
private readonly rangeToExpr: Map<SourceRange, ExternalId>
}
interface SpanMap {
nodes: Map<NodeKey, Ast[]>
tokens: Map<TokenKey, Token>
}
type NodeKey = TokenKey = SourceRangeKey
*/
insight.on("return", function(ctx, frame) {
print(`[return] ctx='${JSON.stringify(ctx)}'`)
print(`[return] frame='${JSON.stringify(frame)}'`)
print(`[return] ctx.returnValue='${ctx.returnValue(frame)}'`)

const p = project

Check warning on line 106 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L106

[@typescript-eslint/no-unused-vars] 'p' is assigned a value but never used. Allowed unused vars must match /^_/u.

Check warning on line 106 in app/ydoc-server-polyglot/src/insight.ts

View workflow job for this annotation

GitHub Actions / 🧹 GUI Lint Results

app/ydoc-server-polyglot/src/insight.ts#L106

[@typescript-eslint/no-unused-vars] 'p' is assigned a value but never used. Allowed unused vars must match /^_/u.
const result = ctx.returnValue(frame)
if (result && spanMap) {
const key = Ast.nodeKey(ctx.charIndex, ctx.charEndIndex - ctx.charIndex)
const asts = spanMap.nodes.get(key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Whenever we get an insight.on('return', ...) event, we look appropriate asts and update them. I am not sure how fast this is, but it certainly seems good enough for demonstration purposes.

if (asts) {
const resultValue = resultToString(result)
for (const ast of asts) {
const editAst = syncModule.getVersion(ast)
if (editAst.widgetMetadata(RETURN_VALUE_KEY) !== resultValue) {
print(`[return] setWidgetMetadata ${ast.code()} : ${resultValue}`)
editAst.setWidgetMetadata(RETURN_VALUE_KEY, resultValue)
}
}
}
}
}, {
expressions: true,
statements: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe n = 42 would be a statement - e.g. if you want to observe when "IDE nodes" are being assigned to, you may want to observe statements.

roots: false,
rootNameFilter: ".*main"
})
5 changes: 4 additions & 1 deletion app/ydoc-server-polyglot/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*.ts"],
"references": [{ "path": "../ydoc-server" }],
"references": [
{ "path": "../ydoc-server" },
{ "path": "../ydoc-shared" },
],
"compilerOptions": {
"composite": true,
"noEmit": false,
Expand Down
2 changes: 1 addition & 1 deletion app/ydoc-shared/src/ast/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { reachable } from '../util/data/graph'
import type { Module } from './mutableModule'
import type { AstId } from './tree'

export { spanMapToIdMap } from './idMap'
export { nodeKey, spanMapToIdMap } from './idMap'
export * from './mutableModule'
export * from './parse'
export { printWithSpans } from './print'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.enso.logging.config.LoggerSetup;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.io.IOAccess;
import org.graalvm.polyglot.io.MessageTransport;
import org.slf4j.event.Level;

Expand Down Expand Up @@ -165,6 +166,7 @@ public Context build() {
Context.newBuilder()
.allowExperimentalOptions(true)
.allowAllAccess(true)
.allowIO(IOAccess.ALL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this good for? I don't think our JavaScript is performing any I/O by itself! The JavaScript code calls into the ydoc-polyfill Java classes and only then the Java classes do some I/O - we shouldn't need a GraalVM permission for that.

.allowHostAccess(allWithTypeMapping())
.option(RuntimeOptions.STRICT_ERRORS, Boolean.toString(strictErrors))
.option(RuntimeOptions.DISABLE_LINTING, Boolean.toString(disableLinting))
Expand Down Expand Up @@ -252,7 +254,7 @@ public Context build() {
}

private static HostAccess allWithTypeMapping() {
return HostAccess.newBuilder()
return HostAccess.newBuilder(HostAccess.EXPLICIT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting with EXPLICIT and then allowing allPublicAccess is strange. Doesn't it create HostAccess.PUBLIC?

.allowPublicAccess(true)
.allowAllImplementations(true)
.allowAllClassImplementations(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,11 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) {
RuntimeOptions.JOB_PARALLELISM,
Runtime.getRuntime.availableProcessors().toString
)
if (System.getProperty("enso.dev.inspectPort") != null) {
val inspectPort = Integer.getInteger("enso.dev.inspectPort", 34567);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary. Just use regular enso --inspect:

    --inspect                              Start the Chrome inspector when
                                           --run is used.

but yeah, I am not sure if it works in "language server" mode. I'd rather got it working then designing new enso.dev.inspectPort property.

extraOptions.put("inspect", s":$inspectPort")
stdErr.attach(arr => System.out.write(arr))
}

val builder = ContextFactory
.create()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.ydoc.polyfill.ParserPolyfill;
import org.enso.ydoc.polyfill.web.WebEnvironment;
import org.graalvm.polyglot.Value;

Expand Down Expand Up @@ -86,6 +87,7 @@ public void log(Level level, String msg, Object... args) {

final void initializePolyfill(Node node, TruffleContext ctx) {
if (!polyfillInitialized) {
var parserPolyfill = new ParserPolyfill();
polyfillInitialized = true;
var exec = Executors.newSingleThreadScheduledExecutor();
Function<URL, Value> eval =
Expand All @@ -99,6 +101,7 @@ final void initializePolyfill(Node node, TruffleContext ctx) {
}
};
WebEnvironment.initialize(eval, exec);
parserPolyfill.initialize(eval);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.util.function.Function;

public final class ParserPolyfill implements ProxyExecutable {

private static final Logger log = LoggerFactory.getLogger(ParserPolyfill.class);
Expand All @@ -22,10 +25,17 @@ public final class ParserPolyfill implements ProxyExecutable {
public ParserPolyfill() {}

public void initialize(Context ctx) {
Source parserJs =
Source.newBuilder("js", ParserPolyfill.class.getResource(PARSER_JS)).buildLiteral();
Function<URL, Value> eval =
(url) -> {
var src = Source.newBuilder("js", url).buildLiteral();
return ctx.eval(src);
};
initialize(eval);
}

ctx.eval(parserJs).execute(this);
public void initialize(Function<URL, Value> eval) {
final Value fn = eval.apply(getClass().getResource(PARSER_JS));
fn.execute(this);
}

@Override
Expand Down
Loading
Loading