From e1e4519be9d7738ce41c3e25314f55af1c4d412d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 13 Nov 2024 07:28:02 +0100 Subject: [PATCH 01/34] quickjs build - take 1 --- .../scripts/esbuild.sh | 4 +- packages/cursorless-jetbrains/TERMINOLOGY.md | 7 +++ packages/cursorless-jetbrains/package.json | 47 +++++++++++++++++++ .../scripts/buildLocal.sh | 5 ++ .../scripts/test-quickjs.sh | 45 ++++++++++++++++++ packages/cursorless-jetbrains/src/index.ts | 7 +++ packages/cursorless-jetbrains/tsconfig.json | 28 +++++++++++ 7 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 packages/cursorless-jetbrains/TERMINOLOGY.md create mode 100644 packages/cursorless-jetbrains/package.json create mode 100644 packages/cursorless-jetbrains/scripts/buildLocal.sh create mode 100644 packages/cursorless-jetbrains/scripts/test-quickjs.sh create mode 100644 packages/cursorless-jetbrains/src/index.ts create mode 100644 packages/cursorless-jetbrains/tsconfig.json diff --git a/packages/cursorless-everywhere-talon/scripts/esbuild.sh b/packages/cursorless-everywhere-talon/scripts/esbuild.sh index ebbf79c1ee..b428a9b5a3 100755 --- a/packages/cursorless-everywhere-talon/scripts/esbuild.sh +++ b/packages/cursorless-everywhere-talon/scripts/esbuild.sh @@ -4,12 +4,12 @@ set -euo pipefail esbuild \ --outfile=out/talon.js \ --platform=neutral \ - --format=esm \ + --format=cjs \ --main-fields=main,module \ --conditions=cursorless:bundler \ --bundle \ --sourcemap \ - --external:talon \ + --external:std \ "$@" # FIXME: Talon javascript files needs to start with an import from Talon before any other code diff --git a/packages/cursorless-jetbrains/TERMINOLOGY.md b/packages/cursorless-jetbrains/TERMINOLOGY.md new file mode 100644 index 0000000000..a8fadf07cc --- /dev/null +++ b/packages/cursorless-jetbrains/TERMINOLOGY.md @@ -0,0 +1,7 @@ +# TextEditor/TextDocument vs Window/Buffer + +1. Each Cursorless "TextDocument" corresponds to a neovim "Buffer" +2. Each Cursorless "TextEditor" corresponds to a neovim "Window" +3. A "TextEditor" corresponds to a view of a "TextDocument". The same "TextDocument" can be opened in two different "TextEditor". +4. When a "Window" changes in neovim, we need to reflect its "TextEditor" +5. When a "Buffer" changes in neovim, we need to reflect its "TextDocument". diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json new file mode 100644 index 0000000000..a7c378d9dc --- /dev/null +++ b/packages/cursorless-jetbrains/package.json @@ -0,0 +1,47 @@ +{ + "name": "@cursorless/cursorless-neovim", + "version": "1.0.0", + "description": "cursorless in neovim", + "main": "./out/index.cjs", + "private": true, + "scripts": { + "build": "pnpm run esbuild:prod && pnpm run populate-dist", + "compile": "tsc --build", + "esbuild:base": "esbuild ./src/index.ts --format=esm --conditions=cursorless:bundler --bundle --main-fields=main,module --outfile=./out/cursorless.js --platform=node --external:std", + "esbuild": "pnpm run esbuild:base --sourcemap", + "esbuild:prod": "pnpm run esbuild:base --minify", + "populate-dist": "bash ./scripts/populate-dist.sh", + "populate-dist:prod": "bash ./scripts/populate-dist.sh", + "watch:tsc": "pnpm compile --watch", + "watch:esbuild": "pnpm esbuild --watch", + "watch": "pnpm run --filter @cursorless/cursorless-neovim --parallel '/^watch:.*/'", + "clean": "rm -rf ./out tsconfig.tsbuildinfo ./dist ./build" + }, + "keywords": [], + "author": "", + "license": "MIT", + "types": "./out/index.d.ts", + "exports": { + ".": { + "cursorless:bundler": "./src/index.ts", + "default": "./out/index.cjs" + } + }, + "dependencies": { + "@cursorless/common": "workspace:*", + "@cursorless/cursorless-engine": "workspace:*", + "@cursorless/neovim-common": "workspace:*", + "@cursorless/neovim-registry": "workspace:*", + "@cursorless/node-common": "workspace:*", + "@cursorless/test-case-recorder": "workspace:*" + }, + "devDependencies": { + "@types/chai": "^5.0.0", + "@types/js-yaml": "^4.0.9", + "@types/lodash": "4.17.10", + "@types/uuid": "^10.0.0", + "lodash": "^4.17.21", + "neovim": "5.3.0", + "vscode-uri": "^3.0.8" + } +} diff --git a/packages/cursorless-jetbrains/scripts/buildLocal.sh b/packages/cursorless-jetbrains/scripts/buildLocal.sh new file mode 100644 index 0000000000..353904ec6a --- /dev/null +++ b/packages/cursorless-jetbrains/scripts/buildLocal.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +npm run esbuild + +cp out/cursorless.js ../../../../cursorless/cursorless-jetbrains/src/main/resources/cursorless/ diff --git a/packages/cursorless-jetbrains/scripts/test-quickjs.sh b/packages/cursorless-jetbrains/scripts/test-quickjs.sh new file mode 100644 index 0000000000..83125aa022 --- /dev/null +++ b/packages/cursorless-jetbrains/scripts/test-quickjs.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail +set -euo pipefail + +QUICKJS_VERSION=2024-01-13 + +echo $ cd out +cd out + +if [[ "$OSTYPE" == "darwin"* ]]; then + brew install quickjs + # Brew doesn't actually publish different versions of the quickjs binary + # brew install quickjs@$QUICKJS_VERSION + + echo $ qjs -I quickjsTest.mjs + qjs -I quickjsTest.mjs + + exit 0 +fi + +QUICKJS_URL_WIN=https://bellard.org/quickjs/binary_releases/quickjs-win-x86_64-$QUICKJS_VERSION.zip +QUICKJS_URL_LINUX=https://bellard.org/quickjs/binary_releases/quickjs-linux-x86_64-$QUICKJS_VERSION.zip +QUICKJS_FILE=quickjs.zip + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + QUICKJS_URL=$QUICKJS_URL_LINUX +elif [[ "$OSTYPE" == "cygwin" ]]; then + QUICKJS_URL=$QUICKJS_URL_WIN +elif [[ "$OSTYPE" == "msys" ]]; then + QUICKJS_URL=$QUICKJS_URL_WIN +elif [[ "$OSTYPE" == "win32" ]]; then + QUICKJS_URL=$QUICKJS_URL_WIN +else + echo "ERROR Unsupported OS: $OSTYPE" + exit 1 +fi + +echo $ curl -o $QUICKJS_FILE $QUICKJS_URL +curl -o $QUICKJS_FILE $QUICKJS_URL + +echo $ unzip $QUICKJS_FILE +unzip $QUICKJS_FILE + +echo $ ./qjs -I cursorless.js +./qjs --module cursorless.js diff --git a/packages/cursorless-jetbrains/src/index.ts b/packages/cursorless-jetbrains/src/index.ts new file mode 100644 index 0000000000..f90bb6a68a --- /dev/null +++ b/packages/cursorless-jetbrains/src/index.ts @@ -0,0 +1,7 @@ + +import { createCursorlessEngine } from "@cursorless/cursorless-engine"; + +export function entry() { + createCursorlessEngine(); + console.log("foo") +} diff --git a/packages/cursorless-jetbrains/tsconfig.json b/packages/cursorless-jetbrains/tsconfig.json new file mode 100644 index 0000000000..ebb84f2ad6 --- /dev/null +++ b/packages/cursorless-jetbrains/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "out" + }, + "references": [ + { + "path": "../common" + }, + { + "path": "../cursorless-engine" + }, + { + "path": "../neovim-common" + }, + { + "path": "../neovim-registry" + }, + { + "path": "../node-common" + }, + { + "path": "../test-case-recorder" + } + ], + "include": ["src/**/*.ts", "src/**/*.json", "../../typings/**/*.d.ts"] +} From 5f5333238c7069a94edc2fdf303d7ffd51a7d7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 13 Nov 2024 18:08:46 +0100 Subject: [PATCH 02/34] wip --- packages/cursorless-jetbrains/package.json | 15 +- .../scripts/buildLocal.sh | 2 +- .../scripts/test-quickjs.sh | 0 .../src/ide/JetbrainsCapabilities.ts | 25 ++ .../src/ide/JetbrainsClient.ts | 3 + .../src/ide/JetbrainsCommandServer.ts | 8 + .../src/ide/JetbrainsEvents.ts | 57 +++ .../src/ide/JetbrainsIDE.ts | 328 ++++++++++++++++++ .../src/ide/JetbrainsPlugin.ts | 14 + packages/cursorless-jetbrains/src/index.ts | 26 +- pnpm-lock.yaml | 42 ++- 11 files changed, 498 insertions(+), 22 deletions(-) mode change 100644 => 100755 packages/cursorless-jetbrains/scripts/buildLocal.sh mode change 100644 => 100755 packages/cursorless-jetbrains/scripts/test-quickjs.sh create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index a7c378d9dc..5733042b9c 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -1,8 +1,8 @@ { - "name": "@cursorless/cursorless-neovim", + "name": "@cursorless/cursorless-jetbrains", "version": "1.0.0", - "description": "cursorless in neovim", - "main": "./out/index.cjs", + "description": "cursorless in jetbrains", + "main": "./out/cursorless.js", "private": true, "scripts": { "build": "pnpm run esbuild:prod && pnpm run populate-dist", @@ -14,7 +14,7 @@ "populate-dist:prod": "bash ./scripts/populate-dist.sh", "watch:tsc": "pnpm compile --watch", "watch:esbuild": "pnpm esbuild --watch", - "watch": "pnpm run --filter @cursorless/cursorless-neovim --parallel '/^watch:.*/'", + "watch": "pnpm run --filter @cursorless/cursorless-jetbrains --parallel '/^watch:.*/'", "clean": "rm -rf ./out tsconfig.tsbuildinfo ./dist ./build" }, "keywords": [], @@ -30,9 +30,6 @@ "dependencies": { "@cursorless/common": "workspace:*", "@cursorless/cursorless-engine": "workspace:*", - "@cursorless/neovim-common": "workspace:*", - "@cursorless/neovim-registry": "workspace:*", - "@cursorless/node-common": "workspace:*", "@cursorless/test-case-recorder": "workspace:*" }, "devDependencies": { @@ -40,8 +37,6 @@ "@types/js-yaml": "^4.0.9", "@types/lodash": "4.17.10", "@types/uuid": "^10.0.0", - "lodash": "^4.17.21", - "neovim": "5.3.0", - "vscode-uri": "^3.0.8" + "lodash": "^4.17.21" } } diff --git a/packages/cursorless-jetbrains/scripts/buildLocal.sh b/packages/cursorless-jetbrains/scripts/buildLocal.sh old mode 100644 new mode 100755 index 353904ec6a..a62a5a00cf --- a/packages/cursorless-jetbrains/scripts/buildLocal.sh +++ b/packages/cursorless-jetbrains/scripts/buildLocal.sh @@ -2,4 +2,4 @@ npm run esbuild -cp out/cursorless.js ../../../../cursorless/cursorless-jetbrains/src/main/resources/cursorless/ +cp out/cursorless.js ../../../cursorless-jetbrains/src/main/resources/cursorless/ diff --git a/packages/cursorless-jetbrains/scripts/test-quickjs.sh b/packages/cursorless-jetbrains/scripts/test-quickjs.sh old mode 100644 new mode 100755 diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts new file mode 100644 index 0000000000..234d51a397 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts @@ -0,0 +1,25 @@ +import type { Capabilities, CommandCapabilityMap } from "@cursorless/common"; + +const COMMAND_CAPABILITIES: CommandCapabilityMap = { + clipboardCopy: { acceptsLocation: false }, + clipboardPaste: true, + toggleLineComment: undefined, + indentLine: undefined, + outdentLine: undefined, + rename: undefined, + quickFix: undefined, + revealDefinition: undefined, + revealTypeDefinition: undefined, + showHover: undefined, + showDebugHover: undefined, + extractVariable: undefined, + fold: undefined, + highlight: { acceptsLocation: true }, + unfold: undefined, + showReferences: undefined, + insertLineAfter: undefined, +}; + +export class JetbrainsCapabilities implements Capabilities { + commands = COMMAND_CAPABILITIES; +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts new file mode 100644 index 0000000000..4aeaeab2c2 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -0,0 +1,3 @@ +export interface JetbrainsClient { + +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts new file mode 100644 index 0000000000..3b78de5e07 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts @@ -0,0 +1,8 @@ +import type { CommandServerApi} from "@cursorless/common"; +import { JetbrainsClient } from "./JetbrainsClient"; + +export class JetbrainsCommandServer implements CommandServerApi { + + + +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts new file mode 100644 index 0000000000..41a521bb41 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts @@ -0,0 +1,57 @@ +import type { + Disposable, + TextDocument, + TextDocumentChangeEvent, + TextDocumentContentChangeEvent, +} from "@cursorless/common"; +import { Position, Range } from "@cursorless/common"; + +export function jetbrainsOnDidChangeTextDocument( + listener: (event: TextDocumentChangeEvent) => void, +): Disposable { + return dummyEvent(); +} + +export function jetbrainsOnDidOpenTextDocument( + listener: (event: TextDocument) => any, + _thisArgs?: any, + _disposables?: Disposable[] | undefined, +): Disposable { + return dummyEvent(); +} + +export function fromJetbrainsContentChange( + document: TextDocument, + buffer: Buffer, + firstLine: number, + lastLine: number, + linedata: string[], +): TextDocumentContentChangeEvent[] { + const result = []; + const text = linedata.join("\n"); + console.debug( + `fromJetbrainsContentChange(): document.getText(): '${document.getText()}'`, + ); + const range = new Range( + new Position(firstLine, 0), + new Position(lastLine - 1, document.lineAt(lastLine - 1).text.length), + ); + const rangeOffset = document.offsetAt(range.start); + const rangeLength = document.offsetAt(range.end) - rangeOffset; + result.push({ + range: range, + rangeOffset: rangeOffset, + rangeLength: rangeLength, + text: text, + }); + console.debug(`fromJetbrainsContentChange(): changes=${JSON.stringify(result)}`); + return result; +} + +function dummyEvent() { + return { + dispose() { + // empty + }, + }; +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts new file mode 100644 index 0000000000..2e0dd54a23 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -0,0 +1,328 @@ +import type { + Disposable, + EditableTextEditor, + IDE, + OpenUntitledTextDocumentOptions, + Range, + RunMode, + Selection, + TextDocumentChangeEvent, + TextEditor, + WorkspaceFolder, +} from "@cursorless/common"; +import type { + Event, + FlashDescriptor, + GeneralizedRange, + QuickPickOptions, + TextDocument, + TextEditorSelectionChangeEvent, + TextEditorVisibleRangesChangeEvent, +} from "@cursorless/common"; +import { pull } from "lodash"; +// import type { Buffer, JetbrainsClient, Window } from "jetbrains"; +// import { v4 as uuid } from "uuid"; +import { JetbrainsCapabilities } from "./JetbrainsCapabilities"; +// import JetbrainsClipboard from "./JetbrainsClipboard"; +// import JetbrainsConfiguration from "./JetbrainsConfiguration"; +// import JetbrainsKeyValueStore from "./JetbrainsKeyValueStore"; +// import JetbrainsMessages from "./JetbrainsMessages"; +// import { JetbrainsTextEditorImpl } from "./JetbrainsTextEditorImpl"; +// import path from "path"; +// import { nodeGetRunMode } from "@cursorless/node-common"; + +import { + jetbrainsOnDidChangeTextDocument, + jetbrainsOnDidOpenTextDocument, +} from "./JetbrainsEvents"; + +export class JetbrainsIDE implements IDE { +// readonly configuration: JetbrainsConfiguration; +// readonly keyValueStore: JetbrainsKeyValueStore; +// readonly messages: JetbrainsMessages; +// readonly clipboard: JetbrainsClipboard; + readonly capabilities: JetbrainsCapabilities; +// private editorMap; +// private documentMap; + private activeWindow: Window | undefined; + private activeBuffer: Buffer | undefined; + + cursorlessVersion: string = "0.0.0"; + workspaceFolders: readonly WorkspaceFolder[] | undefined = undefined; + private disposables: Disposable[] = []; + private assetsRoot_: string | undefined; + private cursorlessJetbrainsPath: string | undefined; + private quickPickReturnValue: string | undefined = undefined; + + constructor(private client: JetbrainsClient) { +// this.configuration = new JetbrainsConfiguration(); +// this.keyValueStore = new JetbrainsKeyValueStore(); +// this.messages = new JetbrainsMessages(); +// this.clipboard = new JetbrainsClipboard(this.client); + this.capabilities = new JetbrainsCapabilities(); +// this.editorMap = new Map(); +// this.documentMap = new Map(); + this.activeWindow = undefined; + this.activeBuffer = undefined; + } + + async init() { +// const rootPath = await getCursorlessNvimPath(this.client); +// // we store the assets into a subfolder of cursorless.nvim +// this.assetsRoot_ = path.join(rootPath, "assets"); +// this.cursorlessJetbrainsPath = path.join( +// rootPath, +// "node", +// "cursorless-jetbrains", +// ); + } + + async showQuickPick( + _items: readonly string[], + _options?: QuickPickOptions, + ): Promise { + throw Error("showQuickPick Not implemented"); + } + + async setHighlightRanges( + _highlightId: string | undefined, + _editor: TextEditor, + _ranges: GeneralizedRange[], + ): Promise { + throw Error("setHighlightRanges Not implemented"); + } + + async flashRanges(_flashDescriptors: FlashDescriptor[]): Promise { + console.debug("flashRanges Not implemented"); + } + + get assetsRoot(): string { + if (this.assetsRoot_ == null) { + throw Error("Field `assetsRoot` has not yet been mocked"); + } + + return this.assetsRoot_; + } + + // + get runMode(): RunMode { + return nodeGetRunMode(); + } + + get activeTextEditor(): TextEditor | undefined { + // throw Error("activeTextEditor Not implemented"); + return this.getActiveTextEditor(); + } + + get activeEditableTextEditor(): EditableTextEditor | undefined { + // throw Error("activeEditableTextEditor Not implemented"); + return this.getActiveTextEditor(); + } + + private getActiveTextEditor() { + const editor = this.activeWindow + ? this.getTextEditor(this.activeWindow) + : undefined; + if (editor === undefined) { + console.debug("getActiveTextEditor: editor is undefined"); + } + return editor; + } + + private getTextEditor(w: Window) { + for (const [window, textEditor] of this.editorMap) { + if (window.id === w.id) { + return textEditor; + } + } + return undefined; + } + + public getTextDocument(b: Buffer) { + for (const [buffer, textDocument] of this.documentMap) { + if (buffer.id === b.id) { + return textDocument; + } + } + return undefined; + } + + get visibleTextEditors(): JetbrainsTextEditorImpl[] { +// return Array.from(this.editorMap.values()); + throw Error("visibleTextEditors Not implemented"); + } + + public getEditableTextEditor(editor: TextEditor): EditableTextEditor { +// return editor as EditableTextEditor; + throw Error("getEditableTextEditor Not implemented"); + } + + public async findInDocument( + _query: string, + _editor: TextEditor, + ): Promise { + throw Error("findInDocument Not implemented"); + } + + public async findInWorkspace(_query: string): Promise { + throw Error("findInWorkspace Not implemented"); + } + + public async openTextDocument(_path: string): Promise { + throw Error("openTextDocument Not implemented"); + } + + public async openUntitledTextDocument( + _options: OpenUntitledTextDocumentOptions, + ): Promise { + throw Error("openUntitledTextDocument Not implemented"); + } + + public async showInputBox(_options?: any): Promise { + throw Error("TextDocumentChangeEvent Not implemented"); + } + + public async executeCommand( + _command: string, + ..._args: any[] + ): Promise { + throw new Error("executeCommand Method not implemented."); + } + + public onDidChangeTextDocument( + listener: (event: TextDocumentChangeEvent) => void, + ): Disposable { + return jetbrainsOnDidChangeTextDocument(listener); + } + + public onDidOpenTextDocument( + listener: (event: TextDocument) => any, + thisArgs?: any, + disposables?: Disposable[] | undefined, + ): Disposable { + return jetbrainsOnDidOpenTextDocument(listener, thisArgs, disposables); + } + onDidCloseTextDocument: Event = dummyEvent; + onDidChangeActiveTextEditor: Event = dummyEvent; + onDidChangeVisibleTextEditors: Event = dummyEvent; + onDidChangeTextEditorSelection: Event = + dummyEvent; + onDidChangeTextEditorVisibleRanges: Event = + dummyEvent; + + /** + * Initialize the current editor (and current document). + * If the current editor already exists, it will only update the current document of that editor. + * + * when we receive our first cursorless command, we will initialize an editor an document for it. + * for the following commands, we will only update the document. + * + * Atm, we only initialize one editor(current window) with one document(current buffer) + */ + async updateTextEditor( + minimal: boolean = false, + ): Promise { + const window = await this.client.window; + const buffer = await window.buffer; + const lines = await buffer.lines; + let linesShown = lines; + if (lines.length >= 30) { + linesShown = lines.slice(0, 15).concat(["..."]).concat(lines.slice(-15)); + } + console.debug( + `updateTextEditor(): window:${window.id}, buffer:${buffer.id}, lines=${JSON.stringify(linesShown)}`, + ); + let selections: Selection[]; + let visibleRanges: Range[]; + if (!minimal) { + selections = await bufferGetSelections(window, this.client); + visibleRanges = await windowGetVisibleRanges(window, this.client, lines); + } else { + selections = []; + visibleRanges = []; + } + const editor = this.toJetbrainsEditor( + window, + buffer, + lines, + visibleRanges, + selections, + ); + + getJetbrainsRegistry().emitEvent("onDidOpenTextDocument", editor.document); + + return editor; + } + + toJetbrainsEditor( + window: Window, + buffer: Buffer, + lines: string[], + visibleRanges: Range[], + selections: Selection[], + ): JetbrainsTextEditorImpl { + let document = this.getTextDocument(buffer); + let editor = this.getTextEditor(window); + if (!document) { + console.debug( + `toJetbrainsEditor(): creating new document: buffer=${buffer.id}`, + ); + document = new JetbrainsTextDocumentImpl( + URI.parse(`jetbrains://${buffer.id}`), // URI.parse(`file://${buffer.id}`), + "plaintext", + 1, + "\n", + // "\r\n", + lines, + ); + this.documentMap.set(buffer, document); + } else { + console.debug(`toJetbrainsEditor(): updating document: buffer=${buffer.id}`); + document.update(lines); + } + if (!editor) { + console.debug( + `toJetbrainsEditor(): creating new editor: window=${window.id}`, + ); + editor = new JetbrainsTextEditorImpl( + uuid(), + this.client, + this, + window, + document, + visibleRanges, + selections, + ); + this.editorMap.set(window, editor); + } else { + console.debug(`toJetbrainsEditor(): updating editor: window=${window.id}`); + editor.updateDocument(visibleRanges, selections, document); + } + this.activeBuffer = buffer; + this.activeWindow = window; + + return this.activeTextEditor as JetbrainsTextEditorImpl; + } + + handleCommandError(err: Error) { + // if (err instanceof OutdatedExtensionError) { + // this.showUpdateExtensionErrorMessage(err); + // } else { + void showErrorMessage(this.client, err.message); + // } + } + + disposeOnExit(...disposables: Disposable[]): () => void { + this.disposables.push(...disposables); + + return () => pull(this.disposables, ...disposables); + } +} + +function dummyEvent() { + return { + dispose() { + // empty + }, + }; +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts new file mode 100644 index 0000000000..4f47906265 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts @@ -0,0 +1,14 @@ +import {JetbrainsClient} from "./JetbrainsClient"; +import {JetbrainsCommandServer} from "./JetbrainsCommandServer"; + +export class JetbrainsPlugin { + readonly client: JetbrainsClient + readonly commandServer: JetbrainsCommandServer + + constructor( + private client: JetbrainsClient, + private commandServer: JetbrainsCommandServer + ) { + } + +} diff --git a/packages/cursorless-jetbrains/src/index.ts b/packages/cursorless-jetbrains/src/index.ts index f90bb6a68a..2702d15a63 100644 --- a/packages/cursorless-jetbrains/src/index.ts +++ b/packages/cursorless-jetbrains/src/index.ts @@ -1,7 +1,25 @@ - +import type { + Command, + CommandServerApi, + Hats, + IDE, + ScopeProvider, +} from "@cursorless/common"; import { createCursorlessEngine } from "@cursorless/cursorless-engine"; +import type { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; +import type { JetbrainsIDE } from "./ide/JetbrainsIDE"; + +export function entry(plugin: JetbrainsPlugin) { + const jetbrainsIDE = new JetbrainsIDE(plugin.client); + createCursorlessEngine({ + ide: jetbrainsIDE, + }); + console.log("entry completed"); +} -export function entry() { - createCursorlessEngine(); - console.log("foo") +export function createPlugin(client: JetbrainsClient): JetbrainsPlugin { + return new JetbrainsPlugin(client); } + +export * from "./ide/JetbrainsPlugin"; +export * from "./ide/JetbrainsIDE"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77e82a0fed..f26b94e788 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ importers: version: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + version: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) eslint-plugin-mocha: specifier: 10.5.0 version: 10.5.0(eslint@8.57.1) @@ -375,6 +375,34 @@ importers: specifier: ^10.7.3 version: 10.7.3 + packages/cursorless-jetbrains: + dependencies: + '@cursorless/common': + specifier: workspace:* + version: link:../common + '@cursorless/cursorless-engine': + specifier: workspace:* + version: link:../cursorless-engine + '@cursorless/test-case-recorder': + specifier: workspace:* + version: link:../test-case-recorder + devDependencies: + '@types/chai': + specifier: ^5.0.0 + version: 5.0.0 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 + '@types/lodash': + specifier: 4.17.10 + version: 4.17.10 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + packages/cursorless-neovim: dependencies: '@cursorless/common': @@ -15433,7 +15461,7 @@ snapshots: eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1) eslint-plugin-react: 7.37.1(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -15462,20 +15490,20 @@ snapshots: debug: 4.3.7(supports-color@8.1.1) enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -15486,7 +15514,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -15497,7 +15525,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 From bec1c52017dcf2d5782670d4ebf5ae88603c85a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 13 Nov 2024 21:59:36 +0100 Subject: [PATCH 03/34] add more jetbrains skeleton --- .../scripts/buildLocal.sh | 2 +- .../cursorless-jetbrains/src/extension.ts | 11 ++ .../src/ide/JetbrainsClipboard.ts | 14 ++ .../src/ide/JetbrainsCommandServer.ts | 2 +- .../src/ide/JetbrainsConfiguration.ts | 61 +++++++ .../src/ide/JetbrainsIDE.ts | 156 +++--------------- .../src/ide/JetbrainsKeyValueStore.ts | 22 +++ .../src/ide/JetbrainsMessages.ts | 12 ++ .../src/ide/JetbrainsPlugin.ts | 10 +- packages/cursorless-jetbrains/src/index.ts | 17 +- 10 files changed, 154 insertions(+), 153 deletions(-) create mode 100644 packages/cursorless-jetbrains/src/extension.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsKeyValueStore.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsMessages.ts diff --git a/packages/cursorless-jetbrains/scripts/buildLocal.sh b/packages/cursorless-jetbrains/scripts/buildLocal.sh index a62a5a00cf..8982d8eeac 100755 --- a/packages/cursorless-jetbrains/scripts/buildLocal.sh +++ b/packages/cursorless-jetbrains/scripts/buildLocal.sh @@ -1,5 +1,5 @@ #!/bin/bash -npm run esbuild +npm run compile && npm run esbuild cp out/cursorless.js ../../../cursorless-jetbrains/src/main/resources/cursorless/ diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts new file mode 100644 index 0000000000..0096550328 --- /dev/null +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -0,0 +1,11 @@ +import { createCursorlessEngine } from "@cursorless/cursorless-engine"; +import { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; +import { JetbrainsIDE } from "./ide/JetbrainsIDE"; + +export function entry(plugin: JetbrainsPlugin) { + const jetbrainsIDE = new JetbrainsIDE(plugin.client); + createCursorlessEngine({ + ide: jetbrainsIDE, + }); + console.log("entry completed"); +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts new file mode 100644 index 0000000000..a08c340c42 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts @@ -0,0 +1,14 @@ +import type { Clipboard } from "@cursorless/common"; +import { JetbrainsClient } from "./JetbrainsClient" + +export class JetbrainsClipboard implements Clipboard { + constructor(private client: JetbrainsClient) {} + + async readText(): Promise { + return ""; + } + + async writeText(value: string): Promise { + return; + } +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts index 3b78de5e07..82bc2ac262 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts @@ -1,7 +1,7 @@ import type { CommandServerApi} from "@cursorless/common"; import { JetbrainsClient } from "./JetbrainsClient"; -export class JetbrainsCommandServer implements CommandServerApi { +export class JetbrainsCommandServer { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts new file mode 100644 index 0000000000..adbea2f11d --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts @@ -0,0 +1,61 @@ + +import { HatStability } from "@cursorless/common"; +import { get } from "lodash"; +import type { + Configuration, + ConfigurationScope, + CursorlessConfiguration, +} from "@cursorless/common"; +import { CONFIGURATION_DEFAULTS } from "@cursorless/common"; +import type { GetFieldType, Paths } from "@cursorless/common"; +import { Notifier } from "@cursorless/common"; + +export class JetbrainsConfiguration implements Configuration { + private notifier = new Notifier(); + + constructor() { + + } + + getOwnConfiguration>( + path: Path, + scope?: ConfigurationScope, + ): GetFieldType { + return get(CONFIGURATION_DEFAULTS, path) as GetFieldType< + CursorlessConfiguration, + Path + >; + } + + onDidChangeConfiguration = this.notifier.registerListener; +} + +/** + * Gets a configuration value from jetbrains, with supported variables expanded. + * For example, `${userHome}` will be expanded to the user's home directory. + * + * We currently only support `${userHome}`. + * + * @param path The path to the configuration value, eg `cursorless.snippetsDir` + * @returns The configuration value, with variables expanded, or undefined if + * the value is not set + */ +export function jetbrainsGetConfigurationString(path: string): string | undefined { + const index = path.lastIndexOf("."); + const section = path.substring(0, index); + const field = path.substring(index + 1); +// const value = "jetbrains.workspace.getConfiguration(section).get(field)"; +// return value != null ? evaluateStringVariables(value) : undefined; + return undefined +} + +function evaluateStringVariables(value: string): string { + return value.replace(/\${(\w+)}/g, (match, variable) => { + switch (variable) { + case "userHome": + return "~/"; + default: + throw Error(`Unknown jetbrains configuration variable '${variable}'`); + } + }); +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 2e0dd54a23..1156e106cb 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -36,11 +36,17 @@ import { jetbrainsOnDidOpenTextDocument, } from "./JetbrainsEvents"; +import { JetbrainsClient } from "./JetbrainsClient" +import { JetbrainsClipboard } from "./JetbrainsClipboard" +import { JetbrainsConfiguration } from "./JetbrainsConfiguration" +import { JetbrainsMessages } from "./JetbrainsMessages" +import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore" + export class JetbrainsIDE implements IDE { -// readonly configuration: JetbrainsConfiguration; -// readonly keyValueStore: JetbrainsKeyValueStore; -// readonly messages: JetbrainsMessages; -// readonly clipboard: JetbrainsClipboard; + readonly configuration: JetbrainsConfiguration; + readonly keyValueStore: JetbrainsKeyValueStore; + readonly messages: JetbrainsMessages; + readonly clipboard: JetbrainsClipboard; readonly capabilities: JetbrainsCapabilities; // private editorMap; // private documentMap; @@ -55,10 +61,10 @@ export class JetbrainsIDE implements IDE { private quickPickReturnValue: string | undefined = undefined; constructor(private client: JetbrainsClient) { -// this.configuration = new JetbrainsConfiguration(); -// this.keyValueStore = new JetbrainsKeyValueStore(); -// this.messages = new JetbrainsMessages(); -// this.clipboard = new JetbrainsClipboard(this.client); + this.configuration = new JetbrainsConfiguration(); + this.keyValueStore = new JetbrainsKeyValueStore(); + this.messages = new JetbrainsMessages(); + this.clipboard = new JetbrainsClipboard(this.client); this.capabilities = new JetbrainsCapabilities(); // this.editorMap = new Map(); // this.documentMap = new Map(); @@ -106,48 +112,22 @@ export class JetbrainsIDE implements IDE { // get runMode(): RunMode { - return nodeGetRunMode(); + return "production"; } get activeTextEditor(): TextEditor | undefined { - // throw Error("activeTextEditor Not implemented"); - return this.getActiveTextEditor(); + throw Error("activeTextEditor Not implemented"); +// return this.getActiveTextEditor(); } get activeEditableTextEditor(): EditableTextEditor | undefined { - // throw Error("activeEditableTextEditor Not implemented"); - return this.getActiveTextEditor(); + throw Error("activeEditableTextEditor Not implemented"); +// return this.getActiveTextEditor(); } - private getActiveTextEditor() { - const editor = this.activeWindow - ? this.getTextEditor(this.activeWindow) - : undefined; - if (editor === undefined) { - console.debug("getActiveTextEditor: editor is undefined"); - } - return editor; - } - - private getTextEditor(w: Window) { - for (const [window, textEditor] of this.editorMap) { - if (window.id === w.id) { - return textEditor; - } - } - return undefined; - } - public getTextDocument(b: Buffer) { - for (const [buffer, textDocument] of this.documentMap) { - if (buffer.id === b.id) { - return textDocument; - } - } - return undefined; - } - get visibleTextEditors(): JetbrainsTextEditorImpl[] { + get visibleTextEditors(): EditableTextEditor[] { // return Array.from(this.editorMap.values()); throw Error("visibleTextEditors Not implemented"); } @@ -210,105 +190,11 @@ export class JetbrainsIDE implements IDE { onDidChangeTextEditorVisibleRanges: Event = dummyEvent; - /** - * Initialize the current editor (and current document). - * If the current editor already exists, it will only update the current document of that editor. - * - * when we receive our first cursorless command, we will initialize an editor an document for it. - * for the following commands, we will only update the document. - * - * Atm, we only initialize one editor(current window) with one document(current buffer) - */ - async updateTextEditor( - minimal: boolean = false, - ): Promise { - const window = await this.client.window; - const buffer = await window.buffer; - const lines = await buffer.lines; - let linesShown = lines; - if (lines.length >= 30) { - linesShown = lines.slice(0, 15).concat(["..."]).concat(lines.slice(-15)); - } - console.debug( - `updateTextEditor(): window:${window.id}, buffer:${buffer.id}, lines=${JSON.stringify(linesShown)}`, - ); - let selections: Selection[]; - let visibleRanges: Range[]; - if (!minimal) { - selections = await bufferGetSelections(window, this.client); - visibleRanges = await windowGetVisibleRanges(window, this.client, lines); - } else { - selections = []; - visibleRanges = []; - } - const editor = this.toJetbrainsEditor( - window, - buffer, - lines, - visibleRanges, - selections, - ); - - getJetbrainsRegistry().emitEvent("onDidOpenTextDocument", editor.document); - - return editor; - } - - toJetbrainsEditor( - window: Window, - buffer: Buffer, - lines: string[], - visibleRanges: Range[], - selections: Selection[], - ): JetbrainsTextEditorImpl { - let document = this.getTextDocument(buffer); - let editor = this.getTextEditor(window); - if (!document) { - console.debug( - `toJetbrainsEditor(): creating new document: buffer=${buffer.id}`, - ); - document = new JetbrainsTextDocumentImpl( - URI.parse(`jetbrains://${buffer.id}`), // URI.parse(`file://${buffer.id}`), - "plaintext", - 1, - "\n", - // "\r\n", - lines, - ); - this.documentMap.set(buffer, document); - } else { - console.debug(`toJetbrainsEditor(): updating document: buffer=${buffer.id}`); - document.update(lines); - } - if (!editor) { - console.debug( - `toJetbrainsEditor(): creating new editor: window=${window.id}`, - ); - editor = new JetbrainsTextEditorImpl( - uuid(), - this.client, - this, - window, - document, - visibleRanges, - selections, - ); - this.editorMap.set(window, editor); - } else { - console.debug(`toJetbrainsEditor(): updating editor: window=${window.id}`); - editor.updateDocument(visibleRanges, selections, document); - } - this.activeBuffer = buffer; - this.activeWindow = window; - - return this.activeTextEditor as JetbrainsTextEditorImpl; - } - handleCommandError(err: Error) { // if (err instanceof OutdatedExtensionError) { // this.showUpdateExtensionErrorMessage(err); // } else { - void showErrorMessage(this.client, err.message); +// void showErrorMessage(this.client, err.message); // } } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsKeyValueStore.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsKeyValueStore.ts new file mode 100644 index 0000000000..ca94a2675f --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsKeyValueStore.ts @@ -0,0 +1,22 @@ +import type { + KeyValueStore, + KeyValueStoreData, + KeyValueStoreKey, +} from "@cursorless/common"; +import { KEY_VALUE_STORE_DEFAULTS } from "@cursorless/common"; + +export class JetbrainsKeyValueStore implements KeyValueStore { + private readonly data: KeyValueStoreData = { ...KEY_VALUE_STORE_DEFAULTS }; + + get(key: K): KeyValueStoreData[K] { + return this.data[key]; + } + + set( + key: K, + value: KeyValueStoreData[K], + ): Promise { + this.data[key] = value; + return Promise.resolve(); + } +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsMessages.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsMessages.ts new file mode 100644 index 0000000000..ac2b77781c --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsMessages.ts @@ -0,0 +1,12 @@ +import type { MessageId, Messages, MessageType } from "@cursorless/common"; + +export class JetbrainsMessages implements Messages { + async showMessage( + _type: MessageType, + _id: MessageId, + _message: string, + ..._options: string[] + ): Promise { + return undefined; + } +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts index 4f47906265..285090a731 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts @@ -2,13 +2,15 @@ import {JetbrainsClient} from "./JetbrainsClient"; import {JetbrainsCommandServer} from "./JetbrainsCommandServer"; export class JetbrainsPlugin { - readonly client: JetbrainsClient - readonly commandServer: JetbrainsCommandServer constructor( - private client: JetbrainsClient, - private commandServer: JetbrainsCommandServer + readonly client: JetbrainsClient, + readonly commandServer: JetbrainsCommandServer ) { } } + +export function createPlugin(client: JetbrainsClient, commandServer: JetbrainsCommandServer): JetbrainsPlugin { + return new JetbrainsPlugin(client, commandServer) +} diff --git a/packages/cursorless-jetbrains/src/index.ts b/packages/cursorless-jetbrains/src/index.ts index 2702d15a63..96cf006b39 100644 --- a/packages/cursorless-jetbrains/src/index.ts +++ b/packages/cursorless-jetbrains/src/index.ts @@ -9,17 +9,10 @@ import { createCursorlessEngine } from "@cursorless/cursorless-engine"; import type { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; import type { JetbrainsIDE } from "./ide/JetbrainsIDE"; -export function entry(plugin: JetbrainsPlugin) { - const jetbrainsIDE = new JetbrainsIDE(plugin.client); - createCursorlessEngine({ - ide: jetbrainsIDE, - }); - console.log("entry completed"); -} - -export function createPlugin(client: JetbrainsClient): JetbrainsPlugin { - return new JetbrainsPlugin(client); -} - export * from "./ide/JetbrainsPlugin"; export * from "./ide/JetbrainsIDE"; +export * from "./extension"; + + + + From 471c428b7c55d3e7bdf83ac7c87df3f7e1f6490c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sat, 16 Nov 2024 08:41:01 +0100 Subject: [PATCH 04/34] wip --- .../scripts/esbuild.sh | 4 +-- packages/cursorless-jetbrains/package.json | 2 +- .../cursorless-jetbrains/src/extension.ts | 8 ++++-- .../src/ide/JetbrainsPlugin.ts | 4 +-- packages/cursorless-jetbrains/src/index.ts | 11 -------- packages/cursorless-jetbrains/src/polyfill.ts | 25 +++++++++++++++++++ packages/cursorless-jetbrains/tsconfig.json | 1 + 7 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 packages/cursorless-jetbrains/src/polyfill.ts diff --git a/packages/cursorless-everywhere-talon/scripts/esbuild.sh b/packages/cursorless-everywhere-talon/scripts/esbuild.sh index b428a9b5a3..ebbf79c1ee 100755 --- a/packages/cursorless-everywhere-talon/scripts/esbuild.sh +++ b/packages/cursorless-everywhere-talon/scripts/esbuild.sh @@ -4,12 +4,12 @@ set -euo pipefail esbuild \ --outfile=out/talon.js \ --platform=neutral \ - --format=cjs \ + --format=esm \ --main-fields=main,module \ --conditions=cursorless:bundler \ --bundle \ --sourcemap \ - --external:std \ + --external:talon \ "$@" # FIXME: Talon javascript files needs to start with an import from Talon before any other code diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index 5733042b9c..f4e27a2dad 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "pnpm run esbuild:prod && pnpm run populate-dist", "compile": "tsc --build", - "esbuild:base": "esbuild ./src/index.ts --format=esm --conditions=cursorless:bundler --bundle --main-fields=main,module --outfile=./out/cursorless.js --platform=node --external:std", + "esbuild:base": "esbuild ./src/index.ts --format=esm --target=es2020 --conditions=cursorless:bundler --bundle --main-fields=main,module --outfile=./out/cursorless.js --platform=neutral --external:std", "esbuild": "pnpm run esbuild:base --sourcemap", "esbuild:prod": "pnpm run esbuild:base --minify", "populate-dist": "bash ./scripts/populate-dist.sh", diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index 0096550328..3ad19edf8a 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -1,11 +1,15 @@ +import "./polyfill"; + +import type { CursorlessEngine } from "@cursorless/cursorless-engine"; import { createCursorlessEngine } from "@cursorless/cursorless-engine"; import { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; import { JetbrainsIDE } from "./ide/JetbrainsIDE"; -export function entry(plugin: JetbrainsPlugin) { +export async function activate(plugin: JetbrainsPlugin): Promise { const jetbrainsIDE = new JetbrainsIDE(plugin.client); - createCursorlessEngine({ + const engine = await createCursorlessEngine({ ide: jetbrainsIDE, }); + return engine console.log("entry completed"); } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts index 285090a731..c619fed530 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts @@ -1,5 +1,5 @@ -import {JetbrainsClient} from "./JetbrainsClient"; -import {JetbrainsCommandServer} from "./JetbrainsCommandServer"; +import type {JetbrainsClient} from "./JetbrainsClient"; +import type {JetbrainsCommandServer} from "./JetbrainsCommandServer"; export class JetbrainsPlugin { diff --git a/packages/cursorless-jetbrains/src/index.ts b/packages/cursorless-jetbrains/src/index.ts index 96cf006b39..0ccc15ecd3 100644 --- a/packages/cursorless-jetbrains/src/index.ts +++ b/packages/cursorless-jetbrains/src/index.ts @@ -1,14 +1,3 @@ -import type { - Command, - CommandServerApi, - Hats, - IDE, - ScopeProvider, -} from "@cursorless/common"; -import { createCursorlessEngine } from "@cursorless/cursorless-engine"; -import type { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; -import type { JetbrainsIDE } from "./ide/JetbrainsIDE"; - export * from "./ide/JetbrainsPlugin"; export * from "./ide/JetbrainsIDE"; export * from "./extension"; diff --git a/packages/cursorless-jetbrains/src/polyfill.ts b/packages/cursorless-jetbrains/src/polyfill.ts new file mode 100644 index 0000000000..f165d08d6e --- /dev/null +++ b/packages/cursorless-jetbrains/src/polyfill.ts @@ -0,0 +1,25 @@ +const global = globalThis as any; + +// process.env is used by `immer` +if (global.process == null) { + global.process = { + env: {}, + }; +} + +// Allows us to use `console.*` with quickjs +// if (typeof print !== "undefined") { +// global.console = { +// log: print, +// error: print, +// warn: print, +// debug: print, +// }; +// } + +// In quickjs `setTimeout` is not available. +// FIXME: Remove dependency on `setTimeout` in the future. +// https://github.com/cursorless-dev/cursorless/issues/2596 +global.setTimeout = (callback: () => void, _delay: number) => { + callback(); +}; diff --git a/packages/cursorless-jetbrains/tsconfig.json b/packages/cursorless-jetbrains/tsconfig.json index ebb84f2ad6..0ac9478318 100644 --- a/packages/cursorless-jetbrains/tsconfig.json +++ b/packages/cursorless-jetbrains/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "target": "ES2020", "rootDir": "src", "outDir": "out" }, From 70fc65386cc68e60d76450ad04bd41e115e7379e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Mon, 18 Nov 2024 06:32:07 +0100 Subject: [PATCH 05/34] wip --- packages/cursorless-jetbrains/src/extension.ts | 8 +++++--- packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 8 -------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index 3ad19edf8a..cb121c0da5 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -5,11 +5,13 @@ import { createCursorlessEngine } from "@cursorless/cursorless-engine"; import { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; import { JetbrainsIDE } from "./ide/JetbrainsIDE"; -export async function activate(plugin: JetbrainsPlugin): Promise { +export async function activate( + plugin: JetbrainsPlugin, +): Promise { const jetbrainsIDE = new JetbrainsIDE(plugin.client); const engine = await createCursorlessEngine({ ide: jetbrainsIDE, }); - return engine - console.log("entry completed"); + return engine; + console.log("activate completed"); } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 1156e106cb..b7a2efbcb5 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -73,14 +73,6 @@ export class JetbrainsIDE implements IDE { } async init() { -// const rootPath = await getCursorlessNvimPath(this.client); -// // we store the assets into a subfolder of cursorless.nvim -// this.assetsRoot_ = path.join(rootPath, "assets"); -// this.cursorlessJetbrainsPath = path.join( -// rootPath, -// "node", -// "cursorless-jetbrains", -// ); } async showQuickPick( From f15e88f3185eb539fee86338db3c1c09d049f17b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sat, 23 Nov 2024 20:14:55 +0100 Subject: [PATCH 06/34] first working hats with javet --- packages/cursorless-jetbrains/package.json | 3 +- .../cursorless-jetbrains/src/extension.ts | 8 +- .../src/ide/JetbrainsClient.ts | 3 +- .../src/ide/JetbrainsEvents.ts | 5 +- .../src/ide/JetbrainsHats.ts | 71 +++++++++ .../src/ide/JetbrainsIDE.ts | 147 +++++++++++++----- .../src/ide/JetbrainsPlugin.ts | 21 +-- 7 files changed, 201 insertions(+), 57 deletions(-) create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index f4e27a2dad..00d563424d 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -30,7 +30,8 @@ "dependencies": { "@cursorless/common": "workspace:*", "@cursorless/cursorless-engine": "workspace:*", - "@cursorless/test-case-recorder": "workspace:*" + "@cursorless/test-case-recorder": "workspace:*", + "vscode-uri": "^3.0.8" }, "devDependencies": { "@types/chai": "^5.0.0", diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index cb121c0da5..ec6305c19c 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -1,16 +1,14 @@ -import "./polyfill"; - import type { CursorlessEngine } from "@cursorless/cursorless-engine"; import { createCursorlessEngine } from "@cursorless/cursorless-engine"; -import { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; +import type { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; import { JetbrainsIDE } from "./ide/JetbrainsIDE"; export async function activate( plugin: JetbrainsPlugin, ): Promise { - const jetbrainsIDE = new JetbrainsIDE(plugin.client); const engine = await createCursorlessEngine({ - ide: jetbrainsIDE, + ide: plugin.ide, + hats: plugin.hats, }); return engine; console.log("activate completed"); diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 4aeaeab2c2..1f99512bc1 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -1,3 +1,4 @@ export interface JetbrainsClient { - + hatsUpdated(hatsJson: string): void; + documentUpdated(updateJson: string): void; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts index 41a521bb41..690d10858e 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts @@ -22,7 +22,6 @@ export function jetbrainsOnDidOpenTextDocument( export function fromJetbrainsContentChange( document: TextDocument, - buffer: Buffer, firstLine: number, lastLine: number, linedata: string[], @@ -44,7 +43,9 @@ export function fromJetbrainsContentChange( rangeLength: rangeLength, text: text, }); - console.debug(`fromJetbrainsContentChange(): changes=${JSON.stringify(result)}`); + console.debug( + `fromJetbrainsContentChange(): changes=${JSON.stringify(result)}`, + ); return result; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts new file mode 100644 index 0000000000..c63095ae17 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -0,0 +1,71 @@ +import type { + Disposable, + HatRange, + Hats, + HatStyleMap, + Listener, +} from "@cursorless/common"; +import { Notifier } from "@cursorless/common"; +import type { JetbrainsClient } from "./JetbrainsClient"; +import { JetbrainsHatRange } from "../types/jetbrains.types"; + +const HAT_COLORS = [ + "default", + "blue", + "green", + "red", + "pink", + "yellow", +] as const; + +export class JetbrainsHats implements Hats { + private isEnabledNotifier: Notifier<[boolean]> = new Notifier(); + private hatRanges: HatRange[] = []; + private client: JetbrainsClient; + + constructor(client: JetbrainsClient) { + this.client = client; + } + + setHatRanges(hatRanges: HatRange[]): Promise { + console.log("ASOEE/CL: JetbrainsHats.setHatRanges : " + hatRanges.length); + + this.hatRanges = hatRanges; + const jbHatRanges = this.toJetbransHatRanges(hatRanges); + const hatsJson = JSON.stringify(jbHatRanges); + console.log("ASOEE/CL: JetbrainsHats.setHatRanges json: " + hatsJson); + this.client.hatsUpdated(hatsJson); + return Promise.resolve(); + } + + toJetbransHatRanges(hatRanges: HatRange[]): JetbrainsHatRange[] { + return hatRanges.map((range) => { + return { + styleName: range.styleName, + editorId: range.editor.id, + range: range.range, + }; + }); + } + + enabledHatStyles: HatStyleMap = Object.fromEntries( + HAT_COLORS.map((color) => [ + color, + { penalty: color === "default" ? 0 : 1 }, + ]), + ); + + onDidChangeEnabledHatStyles(_listener: Listener<[HatStyleMap]>): Disposable { + return { dispose: () => {} }; + } + + isEnabled: boolean = true; + onDidChangeIsEnabled(_listener: Listener<[boolean]>): Disposable { + return { dispose: () => {} }; + } + + toggle(isEnabled?: boolean) { + this.isEnabled = isEnabled ?? !this.isEnabled; + this.isEnabledNotifier.notifyListeners(this.isEnabled); + } +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index b7a2efbcb5..82863a6932 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -1,11 +1,11 @@ -import type { +import { Disposable, EditableTextEditor, IDE, + InMemoryTextDocument, + Notifier, OpenUntitledTextDocumentOptions, - Range, RunMode, - Selection, TextDocumentChangeEvent, TextEditor, WorkspaceFolder, @@ -16,6 +16,7 @@ import type { GeneralizedRange, QuickPickOptions, TextDocument, + TextDocumentContentChangeEvent, TextEditorSelectionChangeEvent, TextEditorVisibleRangesChangeEvent, } from "@cursorless/common"; @@ -32,15 +33,19 @@ import { JetbrainsCapabilities } from "./JetbrainsCapabilities"; // import { nodeGetRunMode } from "@cursorless/node-common"; import { - jetbrainsOnDidChangeTextDocument, + fromJetbrainsContentChange, jetbrainsOnDidOpenTextDocument, } from "./JetbrainsEvents"; -import { JetbrainsClient } from "./JetbrainsClient" -import { JetbrainsClipboard } from "./JetbrainsClipboard" -import { JetbrainsConfiguration } from "./JetbrainsConfiguration" -import { JetbrainsMessages } from "./JetbrainsMessages" -import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore" +import type { JetbrainsClient } from "./JetbrainsClient"; +import { JetbrainsClipboard } from "./JetbrainsClipboard"; +import { JetbrainsConfiguration } from "./JetbrainsConfiguration"; +import { JetbrainsMessages } from "./JetbrainsMessages"; +import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore"; +import type { EditorState } from "../types/types"; +import { URI } from "vscode-uri"; +import { createTextEditor } from "./createTextEditor"; +import { JetbrainsEditor } from "./JetbrainsEditor"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; @@ -48,32 +53,39 @@ export class JetbrainsIDE implements IDE { readonly messages: JetbrainsMessages; readonly clipboard: JetbrainsClipboard; readonly capabilities: JetbrainsCapabilities; -// private editorMap; -// private documentMap; + readonly runMode: RunMode = "development"; + // private editorMap; + // private documentMap; private activeWindow: Window | undefined; private activeBuffer: Buffer | undefined; - cursorlessVersion: string = "0.0.0"; - workspaceFolders: readonly WorkspaceFolder[] | undefined = undefined; private disposables: Disposable[] = []; private assetsRoot_: string | undefined; private cursorlessJetbrainsPath: string | undefined; private quickPickReturnValue: string | undefined = undefined; + private editors: EditableTextEditor[] = []; + + private onDidChangeTextDocumentNotifier: Notifier<[TextDocumentChangeEvent]> = + new Notifier(); + + private onDidChangeTextDocumentContentNotifier: Notifier< + [TextDocumentContentChangeEvent] + > = new Notifier(); + constructor(private client: JetbrainsClient) { this.configuration = new JetbrainsConfiguration(); this.keyValueStore = new JetbrainsKeyValueStore(); this.messages = new JetbrainsMessages(); this.clipboard = new JetbrainsClipboard(this.client); this.capabilities = new JetbrainsCapabilities(); -// this.editorMap = new Map(); -// this.documentMap = new Map(); + // this.editorMap = new Map(); + // this.documentMap = new Map(); this.activeWindow = undefined; this.activeBuffer = undefined; } - async init() { - } + async init() {} async showQuickPick( _items: readonly string[], @@ -95,38 +107,42 @@ export class JetbrainsIDE implements IDE { } get assetsRoot(): string { - if (this.assetsRoot_ == null) { - throw Error("Field `assetsRoot` has not yet been mocked"); - } + console.log("get assetsRoot"); + throw new Error("assetsRoot not implemented."); + } - return this.assetsRoot_; + get cursorlessVersion(): string { + console.log("get cursorlessVersion"); + throw new Error("cursorlessVersion not implemented."); } - // - get runMode(): RunMode { - return "production"; + get workspaceFolders(): readonly WorkspaceFolder[] | undefined { + console.log("get workspaceFolders"); + throw new Error("workspaceFolders not implemented."); } get activeTextEditor(): TextEditor | undefined { - throw Error("activeTextEditor Not implemented"); -// return this.getActiveTextEditor(); + console.log("get activeEditableTextEditor"); + return this.activeEditableTextEditor; } get activeEditableTextEditor(): EditableTextEditor | undefined { - throw Error("activeEditableTextEditor Not implemented"); -// return this.getActiveTextEditor(); + console.log("get activeEditableTextEditor"); + return this.editors[0]; } - - - get visibleTextEditors(): EditableTextEditor[] { -// return Array.from(this.editorMap.values()); - throw Error("visibleTextEditors Not implemented"); + get visibleTextEditors(): TextEditor[] { + console.log("get activeEditableTextEditor"); + return this.editors; } - public getEditableTextEditor(editor: TextEditor): EditableTextEditor { -// return editor as EditableTextEditor; - throw Error("getEditableTextEditor Not implemented"); + getEditableTextEditor(editor: TextEditor): EditableTextEditor { + console.log("getEditableTextEditor"); + if (editor instanceof JetbrainsEditor) { + console.log("getEditableTextEditor - return current"); + return editor; + } + throw Error(`Unsupported text editor type: ${editor}`); } public async findInDocument( @@ -164,7 +180,7 @@ export class JetbrainsIDE implements IDE { public onDidChangeTextDocument( listener: (event: TextDocumentChangeEvent) => void, ): Disposable { - return jetbrainsOnDidChangeTextDocument(listener); + return this.onDidChangeTextDocumentNotifier.registerListener(listener); } public onDidOpenTextDocument( @@ -182,11 +198,11 @@ export class JetbrainsIDE implements IDE { onDidChangeTextEditorVisibleRanges: Event = dummyEvent; - handleCommandError(err: Error) { + handleCommandError(_err: Error) { // if (err instanceof OutdatedExtensionError) { // this.showUpdateExtensionErrorMessage(err); // } else { -// void showErrorMessage(this.client, err.message); + // void showErrorMessage(this.client, err.message); // } } @@ -195,6 +211,55 @@ export class JetbrainsIDE implements IDE { return () => pull(this.disposables, ...disposables); } + + public documentChanged(editorStateJson: any) { + console.log( + "ASOEE/CL: documentChanged : " + JSON.stringify(editorStateJson), + ); + const editorState = editorStateJson as EditorState; + + this.updateTextEditors(editorState); + + const uri = URI.parse("jetbrains://" + editorState); + const language = editorState.languageId + ? editorState.languageId + : "plaintext"; + const document = new InMemoryTextDocument(uri, language, editorState.text); + const linedata = getLines( + editorState.text, + editorState.firstVisibleLine, + editorState.lastVisibleLine, + ); + const contentChangeEvents = fromJetbrainsContentChange( + document, + editorState.firstVisibleLine, + editorState.lastVisibleLine, + linedata, + ); + const documentChangeEvent: TextDocumentChangeEvent = { + document: document, + contentChanges: contentChangeEvents, + }; + console.log("ASOEE/CL: documentChanged : notify..."); + this.emitDidChangeTextDocument(documentChangeEvent); + console.log("ASOEE/CL: documentChanged : notify complete"); + } + + emitDidChangeTextDocument(event: TextDocumentChangeEvent) { + this.onDidChangeTextDocumentNotifier.notifyListeners(event); + } + + updateTextEditors(editorState: EditorState) { + this.editors = [createTextEditor(this.client, this, editorState)]; + console.log( + "ASOEE/CL: updated editor with document " + editorState.firstVisibleLine, + ); + } +} + +function getLines(text: string, firstLine: number, lastLine: number) { + const lines = text.split("\n"); + return lines.slice(firstLine, lastLine); } function dummyEvent() { @@ -204,3 +269,7 @@ function dummyEvent() { }, }; } + +export function createIDE(client: JetbrainsClient) { + return new JetbrainsIDE(client); +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts index c619fed530..5cdc1f1f0f 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsPlugin.ts @@ -1,16 +1,19 @@ -import type {JetbrainsClient} from "./JetbrainsClient"; -import type {JetbrainsCommandServer} from "./JetbrainsCommandServer"; +import type { JetbrainsClient } from "./JetbrainsClient"; +import { JetbrainsHats } from "./JetbrainsHats"; +import type { JetbrainsIDE } from "./JetbrainsIDE"; export class JetbrainsPlugin { - constructor( readonly client: JetbrainsClient, - readonly commandServer: JetbrainsCommandServer - ) { - } - + readonly ide: JetbrainsIDE, + readonly hats: JetbrainsHats, + ) {} } -export function createPlugin(client: JetbrainsClient, commandServer: JetbrainsCommandServer): JetbrainsPlugin { - return new JetbrainsPlugin(client, commandServer) +export function createPlugin( + client: JetbrainsClient, + ide: JetbrainsIDE, +): JetbrainsPlugin { + const hats = new JetbrainsHats(client); + return new JetbrainsPlugin(client, ide, hats); } From d2f1bf91d09bee8f4fcbb6bc875a53ee5009d061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 27 Nov 2024 06:19:49 +0100 Subject: [PATCH 07/34] add setSelection callback --- packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts | 1 + packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 1f99512bc1..44fc6d80e2 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -1,4 +1,5 @@ export interface JetbrainsClient { hatsUpdated(hatsJson: string): void; documentUpdated(updateJson: string): void; + setSelection(editorId: string, selectionJson: string): void; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index c63095ae17..a1fab1384a 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -33,7 +33,7 @@ export class JetbrainsHats implements Hats { this.hatRanges = hatRanges; const jbHatRanges = this.toJetbransHatRanges(hatRanges); const hatsJson = JSON.stringify(jbHatRanges); - console.log("ASOEE/CL: JetbrainsHats.setHatRanges json: " + hatsJson); + // console.log("ASOEE/CL: JetbrainsHats.setHatRanges json: " + hatsJson); this.client.hatsUpdated(hatsJson); return Promise.resolve(); } From e325e62642b61ba685b5bfdb35520baeb38f4b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 27 Nov 2024 06:22:02 +0100 Subject: [PATCH 08/34] add missing files --- .../src/ide/JetbrainsEditor.ts | 161 ++++++++++++++++++ .../src/ide/createTextEditor.ts | 52 ++++++ .../src/ide/jetbrainsPerformEdits.ts | 32 ++++ .../src/ide/setSelections.ts | 19 +++ .../src/testing/JetbrainsTesthelpers.ts | 22 +++ .../src/types/jetbrains.types.ts | 91 ++++++++++ .../cursorless-jetbrains/src/types/types.ts | 42 +++++ 7 files changed, 419 insertions(+) create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts create mode 100644 packages/cursorless-jetbrains/src/ide/createTextEditor.ts create mode 100644 packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts create mode 100644 packages/cursorless-jetbrains/src/ide/setSelections.ts create mode 100644 packages/cursorless-jetbrains/src/testing/JetbrainsTesthelpers.ts create mode 100644 packages/cursorless-jetbrains/src/types/jetbrains.types.ts create mode 100644 packages/cursorless-jetbrains/src/types/types.ts diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts new file mode 100644 index 0000000000..d8fc726b8e --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -0,0 +1,161 @@ +import { + selectionsEqual, + type BreakpointDescriptor, + type Edit, + type EditableTextEditor, + type InMemoryTextDocument, + type OpenLinkOptions, + type Range, + type RevealLineAt, + type Selection, + type SetSelectionsOpts, + type TextEditor, + type TextEditorOptions, +} from "@cursorless/common"; +import { setSelections } from "./setSelections"; +import type { JetbrainsIDE } from "./JetbrainsIDE"; +import { jetbrainsPerformEdits } from "./jetbrainsPerformEdits"; +import type { JetbrainsClient } from "./JetbrainsClient"; + +export class JetbrainsEditor implements EditableTextEditor { + options: TextEditorOptions = { + tabSize: 4, + insertSpaces: true, + }; + + isActive = true; + + constructor( + private client: JetbrainsClient, + private ide: JetbrainsIDE, + public id: string, + public document: InMemoryTextDocument, + public visibleRanges: Range[], + public selections: Selection[], + ) {} + + isEqual(other: TextEditor): boolean { + return this.id === other.id; + } + + async setSelections( + selections: Selection[], + _opts?: SetSelectionsOpts, + ): Promise { + console.log("editor.setSelections"); + if (!selectionsEqual(this.selections, selections)) { + await setSelections(this.client, this.document, this.id, selections); + this.selections = selections; + } + } + + edit(edits: Edit[]): Promise { + console.log("editor.edit"); + jetbrainsPerformEdits(this.client, this.ide, this.document, edits); + return Promise.resolve(true); + } + + async clipboardCopy(_ranges: Range[]): Promise { + throw Error("clipboardCopy not implemented."); + } + + async clipboardPaste(): Promise { + throw Error("clipboardPaste not implemented."); + } + + indentLine(_ranges: Range[]): Promise { + throw Error("indentLine not implemented."); + } + + outdentLine(_ranges: Range[]): Promise { + throw Error("outdentLine not implemented."); + } + + insertLineAfter(_ranges?: Range[]): Promise { + throw Error("insertLineAfter not implemented."); + } + + focus(): Promise { + throw new Error("focus not implemented."); + } + + revealRange(_range: Range): Promise { + return Promise.resolve(); + } + + revealLine(_lineNumber: number, _at: RevealLineAt): Promise { + throw new Error("revealLine not implemented."); + } + + openLink( + _range: Range, + _options?: OpenLinkOptions | undefined, + ): Promise { + throw new Error("openLink not implemented."); + } + + fold(_ranges?: Range[] | undefined): Promise { + throw new Error("fold not implemented."); + } + + unfold(_ranges?: Range[] | undefined): Promise { + throw new Error("unfold not implemented."); + } + + toggleBreakpoint( + _descriptors?: BreakpointDescriptor[] | undefined, + ): Promise { + throw new Error("toggleBreakpoint not implemented."); + } + + toggleLineComment(_ranges?: Range[] | undefined): Promise { + throw new Error("toggleLineComment not implemented."); + } + + insertSnippet( + _snippet: string, + _ranges?: Range[] | undefined, + ): Promise { + throw new Error("insertSnippet not implemented."); + } + + rename(_range?: Range | undefined): Promise { + throw new Error("rename not implemented."); + } + + showReferences(_range?: Range | undefined): Promise { + throw new Error("showReferences not implemented."); + } + + quickFix(_range?: Range | undefined): Promise { + throw new Error("quickFix not implemented."); + } + + revealDefinition(_range?: Range | undefined): Promise { + throw new Error("revealDefinition not implemented."); + } + + revealTypeDefinition(_range?: Range | undefined): Promise { + throw new Error("revealTypeDefinition not implemented."); + } + + showHover(_range?: Range | undefined): Promise { + throw new Error("showHover not implemented."); + } + + showDebugHover(_range?: Range | undefined): Promise { + throw new Error("showDebugHover not implemented."); + } + + extractVariable(_range?: Range | undefined): Promise { + throw new Error("extractVariable not implemented."); + } + + editNewNotebookCellAbove(): Promise<(_selection: Selection) => Selection> { + throw new Error("editNewNotebookCellAbove not implemented."); + } + + editNewNotebookCellBelow(): Promise { + throw new Error("editNewNotebookCellBelow not implemented."); + } +} diff --git a/packages/cursorless-jetbrains/src/ide/createTextEditor.ts b/packages/cursorless-jetbrains/src/ide/createTextEditor.ts new file mode 100644 index 0000000000..18530ee606 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/createTextEditor.ts @@ -0,0 +1,52 @@ +import { + InMemoryTextDocument, + Position, + Selection, + type TextDocument, +} from "@cursorless/common"; +import { URI } from "vscode-uri"; +import type { EditorState, JbPosition, JbSelection } from "../types/types"; +import { JetbrainsEditor } from "./JetbrainsEditor"; +import type { JetbrainsIDE } from "./JetbrainsIDE"; +import type { JetbrainsClient } from "./JetbrainsClient"; + +export function createTextEditor( + client: JetbrainsClient, + ide: JetbrainsIDE, + editorState: EditorState, +): JetbrainsEditor { + console.log("createTextEditor"); + + const id = editorState.id; + const uri = URI.parse(`talon-jetbrains://${id}`); + const languageId = editorState.languageId ?? "plaintext"; + const document = new InMemoryTextDocument(uri, languageId, editorState.text); + const visibleRanges = [document.range]; + const selections = editorState.selections.map((selection) => + createSelection(document, selection), + ); + + return new JetbrainsEditor( + client, + ide, + id, + document, + visibleRanges, + selections, + ); +} + +export function createSelection( + document: TextDocument, + selection: JbSelection, +): Selection { + console.log("createSelection " + JSON.stringify(selection)); + return new Selection( + createPosition(selection.anchor), + createPosition(selection.active), + ); +} + +export function createPosition(jbPosition: JbPosition): Position { + return new Position(jbPosition.line, jbPosition.column); +} diff --git a/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts b/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts new file mode 100644 index 0000000000..58d454689d --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts @@ -0,0 +1,32 @@ +import type { Edit } from "@cursorless/common"; +import { type InMemoryTextDocument } from "@cursorless/common"; +import type { Jetbrains } from "../types/jetbrains.types"; +import type { EditorEdit } from "../types/types"; +import type { JetbrainsIDE } from "./JetbrainsIDE"; +import type { JetbrainsClient } from "./JetbrainsClient"; + +export function jetbrainsPerformEdits( + client: JetbrainsClient, + ide: JetbrainsIDE, + document: InMemoryTextDocument, + edits: Edit[], +) { + const changes = document.edit(edits); + + const editorEdit: EditorEdit = { + text: document.text, + changes: changes.map((change) => ({ + rangeOffset: change.rangeOffset, + rangeLength: change.rangeLength, + text: change.text, + })), + }; + + client.documentUpdated(JSON.stringify(editorEdit)); + //jetbrains.actions.user.cursorless_everywhere_edit_text(editorEdit); + + ide.emitDidChangeTextDocument({ + document, + contentChanges: changes, + }); +} diff --git a/packages/cursorless-jetbrains/src/ide/setSelections.ts b/packages/cursorless-jetbrains/src/ide/setSelections.ts new file mode 100644 index 0000000000..98b6ca6149 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/setSelections.ts @@ -0,0 +1,19 @@ +import type { Selection, TextDocument } from "@cursorless/common"; +import type { Jetbrains } from "../types/jetbrains.types"; +import { JetbrainsClient } from "./JetbrainsClient"; +import { SetSelection } from "../../../cursorless-engine/src/actions/SetSelection"; + +export function setSelections( + client: JetbrainsClient, + document: TextDocument, + editorId: string, + selections: Selection[], +): Promise { + console.log("setSelections: " + selections); + const selectionsJson = JSON.stringify(selections); + console.log("setSelections JSON: " + selectionsJson); + client.setSelection(editorId, selectionsJson); + //jetbrains.actions.user.cursorless_everywhere_set_selections(selectionOffsets); + + return Promise.resolve(); +} diff --git a/packages/cursorless-jetbrains/src/testing/JetbrainsTesthelpers.ts b/packages/cursorless-jetbrains/src/testing/JetbrainsTesthelpers.ts new file mode 100644 index 0000000000..19b4e183bb --- /dev/null +++ b/packages/cursorless-jetbrains/src/testing/JetbrainsTesthelpers.ts @@ -0,0 +1,22 @@ +import type { + Command, + CommandResponse, + IDE, + NormalizedIDE, + TestHelpers, +} from "@cursorless/common"; +import type { JetbrainsIDE } from "../ide/JetbrainsIDE"; +import type { StoredTargetMap } from "@cursorless/cursorless-engine"; + +export interface JetbrainsTestHelpers + extends Omit { + talonJsIDE: JetbrainsIDE; + ide: NormalizedIDE; + storedTargets: StoredTargetMap; + injectIde: (ide: IDE) => void; + runCommand(command: Command): Promise; +} + +export interface ActivateReturnValue { + testHelpers?: JetbrainsTestHelpers; +} diff --git a/packages/cursorless-jetbrains/src/types/jetbrains.types.ts b/packages/cursorless-jetbrains/src/types/jetbrains.types.ts new file mode 100644 index 0000000000..e81eb391c4 --- /dev/null +++ b/packages/cursorless-jetbrains/src/types/jetbrains.types.ts @@ -0,0 +1,91 @@ +import { Range } from "@cursorless/common"; +import type { EditorEdit, EditorState, SelectionOffsets } from "./types"; + +export type JetbrainsNamespace = "user"; + +export interface JetbrainsActions { + app: { + notify(body: string, title: string): void; + }; + clip: { + set_text(text: string): void; + text(): string; + }; + edit: { + find(text?: string): void; + }; + user: { + cursorless_everywhere_get_editor_state(): EditorState; + cursorless_everywhere_set_selections(selections: SelectionOffsets[]): void; + cursorless_everywhere_edit_text(edit: EditorEdit): void; + }; +} + +export interface JetbrainsContextActions { + /** + * Executes an RPC command and waits for the result. + * This function is useful when the result of the command is needed + * immediately after execution. + * + * @param commandId - The identifier of the command to be executed. + * @param command - The command object containing necessary parameters. + * @returns A Promise that resolves with the result of the command execution. + */ + private_cursorless_jetbrains_run_and_wait( + commandId: string, + command: unknown, + ): Promise; + /** + * Executes an RPC command without waiting for the result. + * This function is useful for fire-and-forget operations where + * the result is not immediately needed. + * + * @param commandId - The identifier of the command to be executed. + * @param command - The command object containing necessary parameters. + */ + private_cursorless_jetbrains_run_no_wait( + commandId: string, + command: unknown, + ): void; + /** + * Retrieves the response json from the last RPC command execution. + * + * This is useful because Jetbrains doesn't have a way to read the responses from promises, + * but it does wait for them, so we store the response in a global variable and let it be + * read by this action. + * + * @returns The most recent response from an RPC command (JSON stringified). + */ + private_cursorless_jetbrains_get_response_json(): string; +} + +export interface JetbrainsContext { + matches: string; + tags: string[]; + settings: Record; + lists: Record | string[]>; + action_class(name: "user", actions: JetbrainsContextActions): void; +} + +export interface JetbrainsSettings { + get( + name: string, + defaultValue?: T, + ): T | null; +} + +interface JetbrainsContextConstructor { + new (): JetbrainsContext; +} + +export interface Jetbrains { + readonly actions: JetbrainsActions; + readonly settings: JetbrainsSettings; + Context: JetbrainsContextConstructor; +} + +export interface JetbrainsHatRange { + styleName: string; + editorId: string; + range: Range; +} diff --git a/packages/cursorless-jetbrains/src/types/types.ts b/packages/cursorless-jetbrains/src/types/types.ts new file mode 100644 index 0000000000..6837a5cbe2 --- /dev/null +++ b/packages/cursorless-jetbrains/src/types/types.ts @@ -0,0 +1,42 @@ +export interface SelectionOffsets { + // Document offsets + anchor: number; + active: number; +} + +export interface JbSelection { + start: JbPosition; + end: JbPosition; + cursorPosition: JbPosition; + anchor: JbPosition; + active: JbPosition; +} + +export interface JbPosition { + line: number; + column: number; +} + +export interface EditorState { + id: string; + text: string; + languageId?: string; + firstVisibleLine: number; + lastVisibleLine: number; + selections: JbSelection[]; +} + +export interface EditorChange { + readonly text: string; + readonly rangeOffset: number; + readonly rangeLength: number; +} + +export interface EditorEdit { + /** + * The new document content after the edit. We provide this for platforms + * where we can't easily handle {@link changes}. + */ + text: string; + changes: EditorChange[]; +} From 1a935a680d543b56d34bd42cedf4ada69de7a38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Fri, 29 Nov 2024 20:22:29 +0100 Subject: [PATCH 09/34] fix jetbrains texteditor visible range add multi editor support to jetbrains ide --- .../src/ide/JetbrainsIDE.ts | 25 ++++++++++++------- .../src/ide/createTextEditor.ts | 5 +++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 82863a6932..c42993a43f 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -46,6 +46,7 @@ import type { EditorState } from "../types/types"; import { URI } from "vscode-uri"; import { createTextEditor } from "./createTextEditor"; import { JetbrainsEditor } from "./JetbrainsEditor"; +import { makeNodePairSelection } from "../../../cursorless-engine/src/util/nodeSelectors"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; @@ -56,15 +57,18 @@ export class JetbrainsIDE implements IDE { readonly runMode: RunMode = "development"; // private editorMap; // private documentMap; - private activeWindow: Window | undefined; - private activeBuffer: Buffer | undefined; + private activeProject: Window | undefined; + private activeEditor: Buffer | undefined; private disposables: Disposable[] = []; private assetsRoot_: string | undefined; private cursorlessJetbrainsPath: string | undefined; private quickPickReturnValue: string | undefined = undefined; - private editors: EditableTextEditor[] = []; + private editors: Map = new Map< + string, + JetbrainsEditor + >(); private onDidChangeTextDocumentNotifier: Notifier<[TextDocumentChangeEvent]> = new Notifier(); @@ -81,8 +85,8 @@ export class JetbrainsIDE implements IDE { this.capabilities = new JetbrainsCapabilities(); // this.editorMap = new Map(); // this.documentMap = new Map(); - this.activeWindow = undefined; - this.activeBuffer = undefined; + this.activeProject = undefined; + this.activeEditor = undefined; } async init() {} @@ -128,12 +132,12 @@ export class JetbrainsIDE implements IDE { get activeEditableTextEditor(): EditableTextEditor | undefined { console.log("get activeEditableTextEditor"); - return this.editors[0]; + return [...this.editors.values()].find((editor) => editor.isActive); } get visibleTextEditors(): TextEditor[] { - console.log("get activeEditableTextEditor"); - return this.editors; + console.log("get visibleTextEditors"); + return [...this.editors.values()].filter((editor) => editor.isActive); } getEditableTextEditor(editor: TextEditor): EditableTextEditor { @@ -250,7 +254,10 @@ export class JetbrainsIDE implements IDE { } updateTextEditors(editorState: EditorState) { - this.editors = [createTextEditor(this.client, this, editorState)]; + this.editors.set( + editorState.id, + createTextEditor(this.client, this, editorState), + ); console.log( "ASOEE/CL: updated editor with document " + editorState.firstVisibleLine, ); diff --git a/packages/cursorless-jetbrains/src/ide/createTextEditor.ts b/packages/cursorless-jetbrains/src/ide/createTextEditor.ts index 18530ee606..e8e0982f88 100644 --- a/packages/cursorless-jetbrains/src/ide/createTextEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/createTextEditor.ts @@ -1,6 +1,7 @@ import { InMemoryTextDocument, Position, + Range, Selection, type TextDocument, } from "@cursorless/common"; @@ -21,7 +22,9 @@ export function createTextEditor( const uri = URI.parse(`talon-jetbrains://${id}`); const languageId = editorState.languageId ?? "plaintext"; const document = new InMemoryTextDocument(uri, languageId, editorState.text); - const visibleRanges = [document.range]; + const visibleRanges = [ + new Range(editorState.firstVisibleLine, 0, editorState.lastVisibleLine, 0), + ]; const selections = editorState.selections.map((selection) => createSelection(document, selection), ); From eea280cc03c84102928d40eb903c77c0092b6a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sat, 30 Nov 2024 17:58:36 +0100 Subject: [PATCH 10/34] fix active editor state update existing editors instead of recreating --- .../src/ide/JetbrainsClient.ts | 2 +- .../src/ide/JetbrainsEditor.ts | 2 +- .../src/ide/JetbrainsIDE.ts | 65 +++++++++++++------ .../src/ide/jetbrainsPerformEdits.ts | 3 +- .../cursorless-jetbrains/src/types/types.ts | 1 + 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 44fc6d80e2..ee2aef7428 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -1,5 +1,5 @@ export interface JetbrainsClient { hatsUpdated(hatsJson: string): void; - documentUpdated(updateJson: string): void; + documentUpdated(editorId: string, updateJson: string): void; setSelection(editorId: string, selectionJson: string): void; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index d8fc726b8e..4d03600125 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -51,7 +51,7 @@ export class JetbrainsEditor implements EditableTextEditor { edit(edits: Edit[]): Promise { console.log("editor.edit"); - jetbrainsPerformEdits(this.client, this.ide, this.document, edits); + jetbrainsPerformEdits(this.client, this.ide, this.document, this.id, edits); return Promise.resolve(true); } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index c42993a43f..bbeab8b803 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -5,6 +5,7 @@ import { InMemoryTextDocument, Notifier, OpenUntitledTextDocumentOptions, + Range, RunMode, TextDocumentChangeEvent, TextEditor, @@ -44,9 +45,10 @@ import { JetbrainsMessages } from "./JetbrainsMessages"; import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore"; import type { EditorState } from "../types/types"; import { URI } from "vscode-uri"; -import { createTextEditor } from "./createTextEditor"; +import { createSelection, createTextEditor } from "./createTextEditor"; import { JetbrainsEditor } from "./JetbrainsEditor"; import { makeNodePairSelection } from "../../../cursorless-engine/src/util/nodeSelectors"; +import { elseIfExtractor } from "../../../cursorless-engine/src/languages/elseIfExtractor"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; @@ -58,7 +60,7 @@ export class JetbrainsIDE implements IDE { // private editorMap; // private documentMap; private activeProject: Window | undefined; - private activeEditor: Buffer | undefined; + private activeEditor: JetbrainsEditor | undefined; private disposables: Disposable[] = []; private assetsRoot_: string | undefined; @@ -126,18 +128,24 @@ export class JetbrainsIDE implements IDE { } get activeTextEditor(): TextEditor | undefined { - console.log("get activeEditableTextEditor"); + console.log("get activeTextEditor"); return this.activeEditableTextEditor; } get activeEditableTextEditor(): EditableTextEditor | undefined { console.log("get activeEditableTextEditor"); - return [...this.editors.values()].find((editor) => editor.isActive); + return this.activeEditor; } get visibleTextEditors(): TextEditor[] { console.log("get visibleTextEditors"); - return [...this.editors.values()].filter((editor) => editor.isActive); + //return [...this.editors.values()].filter((editor) => editor.isActive); + if (this.activeEditor) { + console.log("visible: " + this.activeEditor.id); + return [this.activeEditor]; + } else { + return []; + } } getEditableTextEditor(editor: TextEditor): EditableTextEditor { @@ -222,26 +230,21 @@ export class JetbrainsIDE implements IDE { ); const editorState = editorStateJson as EditorState; - this.updateTextEditors(editorState); + const editor = this.updateTextEditors(editorState); - const uri = URI.parse("jetbrains://" + editorState); - const language = editorState.languageId - ? editorState.languageId - : "plaintext"; - const document = new InMemoryTextDocument(uri, language, editorState.text); const linedata = getLines( editorState.text, editorState.firstVisibleLine, editorState.lastVisibleLine, ); const contentChangeEvents = fromJetbrainsContentChange( - document, + editor.document, editorState.firstVisibleLine, editorState.lastVisibleLine, linedata, ); const documentChangeEvent: TextDocumentChangeEvent = { - document: document, + document: editor.document, contentChanges: contentChangeEvents, }; console.log("ASOEE/CL: documentChanged : notify..."); @@ -253,17 +256,37 @@ export class JetbrainsIDE implements IDE { this.onDidChangeTextDocumentNotifier.notifyListeners(event); } - updateTextEditors(editorState: EditorState) { - this.editors.set( - editorState.id, - createTextEditor(this.client, this, editorState), - ); - console.log( - "ASOEE/CL: updated editor with document " + editorState.firstVisibleLine, - ); + updateTextEditors(editorState: EditorState): JetbrainsEditor { + let editor = this.editors.get(editorState.id); + if (editor) { + updateEditor(editor, editorState); + } else { + editor = createTextEditor(this.client, this, editorState); + this.editors.set(editorState.id, editor); + } + if (editorState.active) { + this.activeEditor = editor; + } + return editor; } } +function updateEditor(editor: JetbrainsEditor, editorState: EditorState) { + console.log("Updating editor " + editorState.id); + const oldDocument = editor.document; + editor.document = new InMemoryTextDocument( + oldDocument.uri, + oldDocument.languageId, + editorState.text, + ); + editor.visibleRanges = [ + new Range(editorState.firstVisibleLine, 0, editorState.lastVisibleLine, 0), + ]; + editor.selections = editorState.selections.map((selection) => + createSelection(editor.document, selection), + ); +} + function getLines(text: string, firstLine: number, lastLine: number) { const lines = text.split("\n"); return lines.slice(firstLine, lastLine); diff --git a/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts b/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts index 58d454689d..a031cf1525 100644 --- a/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts +++ b/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts @@ -9,6 +9,7 @@ export function jetbrainsPerformEdits( client: JetbrainsClient, ide: JetbrainsIDE, document: InMemoryTextDocument, + id: string, edits: Edit[], ) { const changes = document.edit(edits); @@ -22,7 +23,7 @@ export function jetbrainsPerformEdits( })), }; - client.documentUpdated(JSON.stringify(editorEdit)); + client.documentUpdated(id, JSON.stringify(editorEdit)); //jetbrains.actions.user.cursorless_everywhere_edit_text(editorEdit); ide.emitDidChangeTextDocument({ diff --git a/packages/cursorless-jetbrains/src/types/types.ts b/packages/cursorless-jetbrains/src/types/types.ts index 6837a5cbe2..99ffd9f490 100644 --- a/packages/cursorless-jetbrains/src/types/types.ts +++ b/packages/cursorless-jetbrains/src/types/types.ts @@ -24,6 +24,7 @@ export interface EditorState { firstVisibleLine: number; lastVisibleLine: number; selections: JbSelection[]; + active: boolean; } export interface EditorChange { From 23caf2d45885fddfbdfb4a0fe465f1b28e294010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 4 Dec 2024 22:08:01 +0100 Subject: [PATCH 11/34] add clipboard support make configuration injectable --- .../src/ide/JetbrainsCapabilities.ts | 2 +- .../src/ide/JetbrainsClient.ts | 2 ++ .../src/ide/JetbrainsClipboard.ts | 15 ++++++++-- .../src/ide/JetbrainsConfiguration.ts | 29 ++++++++++++++----- .../src/ide/JetbrainsEditor.ts | 6 ++-- .../src/ide/JetbrainsIDE.ts | 17 +++++++---- .../src/ide/setSelections.ts | 6 +--- packages/cursorless-jetbrains/src/index.ts | 5 +--- 8 files changed, 53 insertions(+), 29 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts index 234d51a397..36d304e14e 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts @@ -1,7 +1,7 @@ import type { Capabilities, CommandCapabilityMap } from "@cursorless/common"; const COMMAND_CAPABILITIES: CommandCapabilityMap = { - clipboardCopy: { acceptsLocation: false }, + clipboardCopy: { acceptsLocation: true }, clipboardPaste: true, toggleLineComment: undefined, indentLine: undefined, diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index ee2aef7428..1bad322bed 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -1,4 +1,6 @@ export interface JetbrainsClient { + clipboardCopy(editorId: string, rangesJson: string): void; + clipboardPaste(editorId: string): void; hatsUpdated(hatsJson: string): void; documentUpdated(editorId: string, updateJson: string): void; setSelection(editorId: string, selectionJson: string): void; diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts index a08c340c42..1c94b825fd 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClipboard.ts @@ -1,5 +1,5 @@ -import type { Clipboard } from "@cursorless/common"; -import { JetbrainsClient } from "./JetbrainsClient" +import type { Clipboard, Range } from "@cursorless/common"; +import type { JetbrainsClient } from "./JetbrainsClient"; export class JetbrainsClipboard implements Clipboard { constructor(private client: JetbrainsClient) {} @@ -8,7 +8,16 @@ export class JetbrainsClipboard implements Clipboard { return ""; } - async writeText(value: string): Promise { + async writeText(_value: string): Promise { return; } + + async copy(editorId: string, ranges: Range[]): Promise { + const rangesJson = JSON.stringify(ranges); + this.client.clipboardCopy(editorId, rangesJson); + } + + async paste(editorId: string): Promise { + this.client.clipboardPaste(editorId); + } } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts index adbea2f11d..a7633f17e8 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts @@ -1,4 +1,3 @@ - import { HatStability } from "@cursorless/common"; import { get } from "lodash"; import type { @@ -12,22 +11,34 @@ import { Notifier } from "@cursorless/common"; export class JetbrainsConfiguration implements Configuration { private notifier = new Notifier(); + private configuration: CursorlessConfiguration = CONFIGURATION_DEFAULTS; - constructor() { - + constructor(configuration: CursorlessConfiguration) { + this.configuration = configuration; } getOwnConfiguration>( path: Path, scope?: ConfigurationScope, ): GetFieldType { - return get(CONFIGURATION_DEFAULTS, path) as GetFieldType< + return get(this.configuration, path) as GetFieldType< CursorlessConfiguration, Path >; } onDidChangeConfiguration = this.notifier.registerListener; + + updateConfiguration(configuration: CursorlessConfiguration) { + this.configuration = configuration; + this.notifier.notifyListeners(); + } +} + +export function createJetbrainsConfiguration( + configuration: CursorlessConfiguration, +): JetbrainsConfiguration { + return new JetbrainsConfiguration(configuration); } /** @@ -40,13 +51,15 @@ export class JetbrainsConfiguration implements Configuration { * @returns The configuration value, with variables expanded, or undefined if * the value is not set */ -export function jetbrainsGetConfigurationString(path: string): string | undefined { +export function jetbrainsGetConfigurationString( + path: string, +): string | undefined { const index = path.lastIndexOf("."); const section = path.substring(0, index); const field = path.substring(index + 1); -// const value = "jetbrains.workspace.getConfiguration(section).get(field)"; -// return value != null ? evaluateStringVariables(value) : undefined; - return undefined + // const value = "jetbrains.workspace.getConfiguration(section).get(field)"; + // return value != null ? evaluateStringVariables(value) : undefined; + return undefined; } function evaluateStringVariables(value: string): string { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index 4d03600125..f92b842781 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -55,12 +55,12 @@ export class JetbrainsEditor implements EditableTextEditor { return Promise.resolve(true); } - async clipboardCopy(_ranges: Range[]): Promise { - throw Error("clipboardCopy not implemented."); + async clipboardCopy(ranges: Range[]): Promise { + await this.ide.clipboard.copy(this.id, ranges); } async clipboardPaste(): Promise { - throw Error("clipboardPaste not implemented."); + await this.ide.clipboard.paste(this.id); } indentLine(_ranges: Range[]): Promise { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index bbeab8b803..d9856f1f0a 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -49,6 +49,7 @@ import { createSelection, createTextEditor } from "./createTextEditor"; import { JetbrainsEditor } from "./JetbrainsEditor"; import { makeNodePairSelection } from "../../../cursorless-engine/src/util/nodeSelectors"; import { elseIfExtractor } from "../../../cursorless-engine/src/languages/elseIfExtractor"; +import { Configuration } from "../../../common/src/ide/types/Configuration"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; @@ -79,8 +80,11 @@ export class JetbrainsIDE implements IDE { [TextDocumentContentChangeEvent] > = new Notifier(); - constructor(private client: JetbrainsClient) { - this.configuration = new JetbrainsConfiguration(); + constructor( + private client: JetbrainsClient, + configuration: JetbrainsConfiguration, + ) { + this.configuration = configuration; this.keyValueStore = new JetbrainsKeyValueStore(); this.messages = new JetbrainsMessages(); this.clipboard = new JetbrainsClipboard(this.client); @@ -179,7 +183,7 @@ export class JetbrainsIDE implements IDE { } public async showInputBox(_options?: any): Promise { - throw Error("TextDocumentChangeEvent Not implemented"); + throw Error("showInputBox Not implemented"); } public async executeCommand( @@ -300,6 +304,9 @@ function dummyEvent() { }; } -export function createIDE(client: JetbrainsClient) { - return new JetbrainsIDE(client); +export function createIDE( + client: JetbrainsClient, + configuration: JetbrainsConfiguration, +) { + return new JetbrainsIDE(client, configuration); } diff --git a/packages/cursorless-jetbrains/src/ide/setSelections.ts b/packages/cursorless-jetbrains/src/ide/setSelections.ts index 98b6ca6149..39f7413d9d 100644 --- a/packages/cursorless-jetbrains/src/ide/setSelections.ts +++ b/packages/cursorless-jetbrains/src/ide/setSelections.ts @@ -1,7 +1,5 @@ import type { Selection, TextDocument } from "@cursorless/common"; -import type { Jetbrains } from "../types/jetbrains.types"; -import { JetbrainsClient } from "./JetbrainsClient"; -import { SetSelection } from "../../../cursorless-engine/src/actions/SetSelection"; +import type { JetbrainsClient } from "./JetbrainsClient"; export function setSelections( client: JetbrainsClient, @@ -13,7 +11,5 @@ export function setSelections( const selectionsJson = JSON.stringify(selections); console.log("setSelections JSON: " + selectionsJson); client.setSelection(editorId, selectionsJson); - //jetbrains.actions.user.cursorless_everywhere_set_selections(selectionOffsets); - return Promise.resolve(); } diff --git a/packages/cursorless-jetbrains/src/index.ts b/packages/cursorless-jetbrains/src/index.ts index 0ccc15ecd3..0c195aa550 100644 --- a/packages/cursorless-jetbrains/src/index.ts +++ b/packages/cursorless-jetbrains/src/index.ts @@ -1,7 +1,4 @@ export * from "./ide/JetbrainsPlugin"; +export * from "./ide/JetbrainsConfiguration"; export * from "./ide/JetbrainsIDE"; export * from "./extension"; - - - - From 9bedadd8fcc4f4255f27bf34e293a85fa0e72a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Thu, 5 Dec 2024 06:26:59 +0100 Subject: [PATCH 12/34] remove editor state on close --- packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index d9856f1f0a..1169b276ff 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -228,6 +228,16 @@ export class JetbrainsIDE implements IDE { return () => pull(this.disposables, ...disposables); } + public documentClosed(editorId: string) { + this.editors.delete(editorId); + console.log( + "removed editor " + + editorId + + "remaining after change: " + + this.editors.size, + ); + } + public documentChanged(editorStateJson: any) { console.log( "ASOEE/CL: documentChanged : " + JSON.stringify(editorStateJson), From f16e5952d732bf9f9baa2a1a051471ac44512968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Thu, 5 Dec 2024 09:07:07 +0100 Subject: [PATCH 13/34] remove debug logging --- .../src/ide/JetbrainsIDE.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 1169b276ff..5d7e0fafd1 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -132,17 +132,17 @@ export class JetbrainsIDE implements IDE { } get activeTextEditor(): TextEditor | undefined { - console.log("get activeTextEditor"); + // console.log("get activeTextEditor"); return this.activeEditableTextEditor; } get activeEditableTextEditor(): EditableTextEditor | undefined { - console.log("get activeEditableTextEditor"); + // console.log("get activeEditableTextEditor"); return this.activeEditor; } get visibleTextEditors(): TextEditor[] { - console.log("get visibleTextEditors"); + // console.log("get visibleTextEditors"); //return [...this.editors.values()].filter((editor) => editor.isActive); if (this.activeEditor) { console.log("visible: " + this.activeEditor.id); @@ -153,7 +153,7 @@ export class JetbrainsIDE implements IDE { } getEditableTextEditor(editor: TextEditor): EditableTextEditor { - console.log("getEditableTextEditor"); + // console.log("getEditableTextEditor"); if (editor instanceof JetbrainsEditor) { console.log("getEditableTextEditor - return current"); return editor; @@ -230,18 +230,18 @@ export class JetbrainsIDE implements IDE { public documentClosed(editorId: string) { this.editors.delete(editorId); - console.log( - "removed editor " + - editorId + - "remaining after change: " + - this.editors.size, - ); + // console.log( + // "removed editor " + + // editorId + + // "remaining after change: " + + // this.editors.size, + // ); } public documentChanged(editorStateJson: any) { - console.log( - "ASOEE/CL: documentChanged : " + JSON.stringify(editorStateJson), - ); + // console.log( + // "ASOEE/CL: documentChanged : " + JSON.stringify(editorStateJson), + // ); const editorState = editorStateJson as EditorState; const editor = this.updateTextEditors(editorState); @@ -261,9 +261,9 @@ export class JetbrainsIDE implements IDE { document: editor.document, contentChanges: contentChangeEvents, }; - console.log("ASOEE/CL: documentChanged : notify..."); + // console.log("ASOEE/CL: documentChanged : notify..."); this.emitDidChangeTextDocument(documentChangeEvent); - console.log("ASOEE/CL: documentChanged : notify complete"); + // console.log("ASOEE/CL: documentChanged : notify complete"); } emitDidChangeTextDocument(event: TextDocumentChangeEvent) { @@ -286,7 +286,7 @@ export class JetbrainsIDE implements IDE { } function updateEditor(editor: JetbrainsEditor, editorState: EditorState) { - console.log("Updating editor " + editorState.id); + // console.log("Updating editor " + editorState.id); const oldDocument = editor.document; editor.document = new InMemoryTextDocument( oldDocument.uri, From b63c8c3b437bc28cf0b78ede76cae9b1aaad3789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sat, 7 Dec 2024 18:15:19 +0100 Subject: [PATCH 14/34] add most of range IDE commands --- .../src/ide/JetbrainsCapabilities.ts | 24 ++-- .../src/ide/JetbrainsClient.ts | 3 + .../src/ide/JetbrainsEditor.ts | 135 +++++++++++++----- 3 files changed, 116 insertions(+), 46 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts index 36d304e14e..8d4d8b1f14 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCapabilities.ts @@ -3,21 +3,21 @@ import type { Capabilities, CommandCapabilityMap } from "@cursorless/common"; const COMMAND_CAPABILITIES: CommandCapabilityMap = { clipboardCopy: { acceptsLocation: true }, clipboardPaste: true, - toggleLineComment: undefined, - indentLine: undefined, - outdentLine: undefined, - rename: undefined, - quickFix: undefined, - revealDefinition: undefined, - revealTypeDefinition: undefined, + toggleLineComment: { acceptsLocation: true }, + indentLine: { acceptsLocation: true }, + outdentLine: { acceptsLocation: true }, + rename: { acceptsLocation: true }, + quickFix: { acceptsLocation: true }, + revealDefinition: { acceptsLocation: true }, + revealTypeDefinition: { acceptsLocation: true }, showHover: undefined, showDebugHover: undefined, - extractVariable: undefined, - fold: undefined, + extractVariable: { acceptsLocation: true }, + fold: { acceptsLocation: true }, highlight: { acceptsLocation: true }, - unfold: undefined, - showReferences: undefined, - insertLineAfter: undefined, + unfold: { acceptsLocation: true }, + showReferences: { acceptsLocation: true }, + insertLineAfter: { acceptsLocation: true }, }; export class JetbrainsCapabilities implements Capabilities { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 1bad322bed..5002d558b0 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -4,4 +4,7 @@ export interface JetbrainsClient { hatsUpdated(hatsJson: string): void; documentUpdated(editorId: string, updateJson: string): void; setSelection(editorId: string, selectionJson: string): void; + executeCommand(editorId: string, command: string, jsonArgs: string): string; + executeRangeCommand(editorId: string, commandJson: string): string; + insertLineAfter(editorId: string, rangesJson: string): void; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index f92b842781..c0f11bd375 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -1,3 +1,4 @@ +import type { Selection } from "@cursorless/common"; import { selectionsEqual, type BreakpointDescriptor, @@ -7,7 +8,6 @@ import { type OpenLinkOptions, type Range, type RevealLineAt, - type Selection, type SetSelectionsOpts, type TextEditor, type TextEditorOptions, @@ -16,6 +16,7 @@ import { setSelections } from "./setSelections"; import type { JetbrainsIDE } from "./JetbrainsIDE"; import { jetbrainsPerformEdits } from "./jetbrainsPerformEdits"; import type { JetbrainsClient } from "./JetbrainsClient"; +import { JetbrainsEditorCommand } from "./JetbrainsEditorCommand"; export class JetbrainsEditor implements EditableTextEditor { options: TextEditorOptions = { @@ -63,16 +64,28 @@ export class JetbrainsEditor implements EditableTextEditor { await this.ide.clipboard.paste(this.id); } - indentLine(_ranges: Range[]): Promise { - throw Error("indentLine not implemented."); + async indentLine(ranges: Range[]): Promise { + const command = new JetbrainsEditorCommand( + ranges ? ranges : [], + true, + true, + "EditorIndentSelection", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } - outdentLine(_ranges: Range[]): Promise { - throw Error("outdentLine not implemented."); + async outdentLine(ranges: Range[]): Promise { + const command = new JetbrainsEditorCommand( + ranges ? ranges : [], + true, + true, + "EditorUnindentSelection", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } - insertLineAfter(_ranges?: Range[]): Promise { - throw Error("insertLineAfter not implemented."); + async insertLineAfter(ranges?: Range[]): Promise { + await this.client.insertLineAfter(this.id, JSON.stringify(ranges)); } focus(): Promise { @@ -94,22 +107,40 @@ export class JetbrainsEditor implements EditableTextEditor { throw new Error("openLink not implemented."); } - fold(_ranges?: Range[] | undefined): Promise { - throw new Error("fold not implemented."); + async fold(ranges?: Range[] | undefined): Promise { + const command = new JetbrainsEditorCommand( + ranges ? ranges : [], + true, + false, + "CollapseRegion", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } - unfold(_ranges?: Range[] | undefined): Promise { - throw new Error("unfold not implemented."); + async unfold(ranges?: Range[] | undefined): Promise { + const command = new JetbrainsEditorCommand( + ranges ? ranges : [], + true, + false, + "ExpandRegion", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } - toggleBreakpoint( + async toggleBreakpoint( _descriptors?: BreakpointDescriptor[] | undefined, ): Promise { throw new Error("toggleBreakpoint not implemented."); } - toggleLineComment(_ranges?: Range[] | undefined): Promise { - throw new Error("toggleLineComment not implemented."); + async toggleLineComment(ranges?: Range[] | undefined): Promise { + const command = new JetbrainsEditorCommand( + ranges ? ranges : [], + true, + false, + "CommentByLineComment", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } insertSnippet( @@ -119,24 +150,54 @@ export class JetbrainsEditor implements EditableTextEditor { throw new Error("insertSnippet not implemented."); } - rename(_range?: Range | undefined): Promise { - throw new Error("rename not implemented."); - } - - showReferences(_range?: Range | undefined): Promise { - throw new Error("showReferences not implemented."); - } - - quickFix(_range?: Range | undefined): Promise { - throw new Error("quickFix not implemented."); - } - - revealDefinition(_range?: Range | undefined): Promise { - throw new Error("revealDefinition not implemented."); - } - - revealTypeDefinition(_range?: Range | undefined): Promise { - throw new Error("revealTypeDefinition not implemented."); + async rename(range?: Range | undefined): Promise { + const command = new JetbrainsEditorCommand( + range ? [range] : [], + true, + false, + "RenameElement", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); + } + + async showReferences(range?: Range | undefined): Promise { + const command = new JetbrainsEditorCommand( + range ? [range] : [], + true, + false, + "FindUsages", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); + } + + async quickFix(range?: Range | undefined): Promise { + const command = new JetbrainsEditorCommand( + range ? [range] : [], + true, + false, + "ShowIntentionActions", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); + } + + async revealDefinition(range?: Range | undefined): Promise { + const command = new JetbrainsEditorCommand( + range ? [range] : [], + true, + false, + "QuickImplementations", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); + } + + async revealTypeDefinition(range?: Range | undefined): Promise { + const command = new JetbrainsEditorCommand( + range ? [range] : [], + true, + false, + "QuickImplementations", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } showHover(_range?: Range | undefined): Promise { @@ -147,8 +208,14 @@ export class JetbrainsEditor implements EditableTextEditor { throw new Error("showDebugHover not implemented."); } - extractVariable(_range?: Range | undefined): Promise { - throw new Error("extractVariable not implemented."); + async extractVariable(range?: Range | undefined): Promise { + const command = new JetbrainsEditorCommand( + range ? [range] : [], + true, + false, + "IntroduceVariable", + ); + await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } editNewNotebookCellAbove(): Promise<(_selection: Selection) => Selection> { From 203e64d26a852315073605b7ec5e46ec97037b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sat, 7 Dec 2024 19:35:36 +0100 Subject: [PATCH 15/34] add reveal range --- packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts | 1 + packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 5002d558b0..42ee7eb386 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -7,4 +7,5 @@ export interface JetbrainsClient { executeCommand(editorId: string, command: string, jsonArgs: string): string; executeRangeCommand(editorId: string, commandJson: string): string; insertLineAfter(editorId: string, rangesJson: string): void; + revealLine(editorId: string, line: number, revealAt: string): void; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index c0f11bd375..8089ca320a 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -96,8 +96,8 @@ export class JetbrainsEditor implements EditableTextEditor { return Promise.resolve(); } - revealLine(_lineNumber: number, _at: RevealLineAt): Promise { - throw new Error("revealLine not implemented."); + async revealLine(lineNumber: number, at: RevealLineAt): Promise { + await this.client.revealLine(this.id, lineNumber, at); } openLink( From 52315e2a1d37e5024af1d67bc211cb6e1e66aad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Mon, 9 Dec 2024 21:43:04 +0100 Subject: [PATCH 16/34] add jetbrains treesitter support --- packages/cursorless-jetbrains/package.json | 5 +- .../cursorless-jetbrains/src/extension.ts | 19 +++++- .../src/ide/JetbrainsClient.ts | 1 + .../src/ide/JetbrainsEditorCommand.ts | 10 ++++ .../src/ide/JetbrainsIDE.ts | 59 +++++++++---------- .../src/ide/JetbrainsTreeSitter.ts | 52 ++++++++++++++++ .../ide/JetbrainsTreeSitterQueryProvider.ts | 27 +++++++++ packages/cursorless-jetbrains/tsconfig.json | 2 +- 8 files changed, 139 insertions(+), 36 deletions(-) create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsEditorCommand.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index 00d563424d..a06aad6639 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "pnpm run esbuild:prod && pnpm run populate-dist", "compile": "tsc --build", - "esbuild:base": "esbuild ./src/index.ts --format=esm --target=es2020 --conditions=cursorless:bundler --bundle --main-fields=main,module --outfile=./out/cursorless.js --platform=neutral --external:std", + "esbuild:base": "esbuild ./src/index.ts --format=esm --target=es2020 --conditions=cursorless:bundler --bundle --main-fields=main,module --outfile=./out/cursorless.js --platform=neutral --external:std --external:fs --external:path", "esbuild": "pnpm run esbuild:base --sourcemap", "esbuild:prod": "pnpm run esbuild:base --minify", "populate-dist": "bash ./scripts/populate-dist.sh", @@ -31,7 +31,8 @@ "@cursorless/common": "workspace:*", "@cursorless/cursorless-engine": "workspace:*", "@cursorless/test-case-recorder": "workspace:*", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.0.8", + "web-tree-sitter": "0.24.4" }, "devDependencies": { "@types/chai": "^5.0.0", diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index ec6305c19c..2b74f05f20 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -1,15 +1,30 @@ import type { CursorlessEngine } from "@cursorless/cursorless-engine"; import { createCursorlessEngine } from "@cursorless/cursorless-engine"; import type { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; -import { JetbrainsIDE } from "./ide/JetbrainsIDE"; +import Parser from "web-tree-sitter"; +import { JetbrainsTreeSitter } from "./ide/JetbrainsTreeSitter"; +import { JetbrainsTreeSitterQueryProvider } from "./ide/JetbrainsTreeSitterQueryProvider"; export async function activate( plugin: JetbrainsPlugin, + wasmDirectory: string, ): Promise { + console.log("activate started"); + await Parser.init({ + locateFile(scriptName: string, _scriptDirectory: string) { + console.log("locateFile called for " + scriptName); + return wasmDirectory + scriptName; + }, + }); + console.log("Parser initialized"); + + const queryProvider = new JetbrainsTreeSitterQueryProvider(plugin.ide); const engine = await createCursorlessEngine({ ide: plugin.ide, hats: plugin.hats, + treeSitterQueryProvider: queryProvider, + treeSitter: new JetbrainsTreeSitter(wasmDirectory), }); - return engine; console.log("activate completed"); + return engine; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 42ee7eb386..d08859953a 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -8,4 +8,5 @@ export interface JetbrainsClient { executeRangeCommand(editorId: string, commandJson: string): string; insertLineAfter(editorId: string, rangesJson: string): void; revealLine(editorId: string, line: number, revealAt: string): void; + readQuery(filename: string): string | undefined; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditorCommand.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditorCommand.ts new file mode 100644 index 0000000000..9a057b9c10 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditorCommand.ts @@ -0,0 +1,10 @@ +import type { Range } from "@cursorless/common"; + +export class JetbrainsEditorCommand { + constructor( + private ranges: Range[], + private singleRange: boolean, + private restoreSelection: boolean, + private ideCommand: string, + ) {} +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 5d7e0fafd1..c1a1ba6aed 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -1,16 +1,4 @@ -import { - Disposable, - EditableTextEditor, - IDE, - InMemoryTextDocument, - Notifier, - OpenUntitledTextDocumentOptions, - Range, - RunMode, - TextDocumentChangeEvent, - TextEditor, - WorkspaceFolder, -} from "@cursorless/common"; +import { InMemoryTextDocument, Notifier, Range } from "@cursorless/common"; import type { Event, FlashDescriptor, @@ -20,19 +8,17 @@ import type { TextDocumentContentChangeEvent, TextEditorSelectionChangeEvent, TextEditorVisibleRangesChangeEvent, + Disposable, + EditableTextEditor, + IDE, + OpenUntitledTextDocumentOptions, + RunMode, + TextDocumentChangeEvent, + TextEditor, + WorkspaceFolder, } from "@cursorless/common"; import { pull } from "lodash"; -// import type { Buffer, JetbrainsClient, Window } from "jetbrains"; -// import { v4 as uuid } from "uuid"; import { JetbrainsCapabilities } from "./JetbrainsCapabilities"; -// import JetbrainsClipboard from "./JetbrainsClipboard"; -// import JetbrainsConfiguration from "./JetbrainsConfiguration"; -// import JetbrainsKeyValueStore from "./JetbrainsKeyValueStore"; -// import JetbrainsMessages from "./JetbrainsMessages"; -// import { JetbrainsTextEditorImpl } from "./JetbrainsTextEditorImpl"; -// import path from "path"; -// import { nodeGetRunMode } from "@cursorless/node-common"; - import { fromJetbrainsContentChange, jetbrainsOnDidOpenTextDocument, @@ -40,16 +26,12 @@ import { import type { JetbrainsClient } from "./JetbrainsClient"; import { JetbrainsClipboard } from "./JetbrainsClipboard"; -import { JetbrainsConfiguration } from "./JetbrainsConfiguration"; +import type { JetbrainsConfiguration } from "./JetbrainsConfiguration"; import { JetbrainsMessages } from "./JetbrainsMessages"; import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore"; import type { EditorState } from "../types/types"; -import { URI } from "vscode-uri"; import { createSelection, createTextEditor } from "./createTextEditor"; import { JetbrainsEditor } from "./JetbrainsEditor"; -import { makeNodePairSelection } from "../../../cursorless-engine/src/util/nodeSelectors"; -import { elseIfExtractor } from "../../../cursorless-engine/src/languages/elseIfExtractor"; -import { Configuration } from "../../../common/src/ide/types/Configuration"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; @@ -75,6 +57,8 @@ export class JetbrainsIDE implements IDE { private onDidChangeTextDocumentNotifier: Notifier<[TextDocumentChangeEvent]> = new Notifier(); + private onDidOpenTextDocumentNotifier: Notifier<[TextDocument]> = + new Notifier(); private onDidChangeTextDocumentContentNotifier: Notifier< [TextDocumentContentChangeEvent] @@ -201,10 +185,10 @@ export class JetbrainsIDE implements IDE { public onDidOpenTextDocument( listener: (event: TextDocument) => any, - thisArgs?: any, - disposables?: Disposable[] | undefined, + _thisArgs?: any, + _disposables?: Disposable[] | undefined, ): Disposable { - return jetbrainsOnDidOpenTextDocument(listener, thisArgs, disposables); + return this.onDidOpenTextDocumentNotifier.registerListener(listener); } onDidCloseTextDocument: Event = dummyEvent; onDidChangeActiveTextEditor: Event = dummyEvent; @@ -238,6 +222,15 @@ export class JetbrainsIDE implements IDE { // ); } + public documentCreated(editorStateJson: any) { + this.documentChanged(editorStateJson); + const editorState = editorStateJson as EditorState; + const editor = this.editors.get(editorState.id); + if (editor) { + this.onDidOpenTextDocumentNotifier.notifyListeners(editor.document); + } + } + public documentChanged(editorStateJson: any) { // console.log( // "ASOEE/CL: documentChanged : " + JSON.stringify(editorStateJson), @@ -283,6 +276,10 @@ export class JetbrainsIDE implements IDE { } return editor; } + + readQuery(filename: string): string | undefined { + return this.client.readQuery(filename); + } } function updateEditor(editor: JetbrainsEditor, editorState: EditorState) { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts new file mode 100644 index 0000000000..89c089ae86 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts @@ -0,0 +1,52 @@ +import type { Range, TextDocument, TreeSitter } from "@cursorless/common"; +import type { Language, SyntaxNode, Tree } from "web-tree-sitter"; +import Parser from "web-tree-sitter"; + +export class JetbrainsTreeSitter implements TreeSitter { + constructor(private wasmDirectory: string) {} + + parsers = new Map(); + + getTree(document: TextDocument): Tree { + console.log(`get tree ${document.languageId}`); + if (this.getLanguage(document.languageId)) { + const parser = this.parsers.get(document.languageId); + if (parser) { + return parser.parse(document.getText()); + } + } + throw new Error("Language not supported"); + } + + async loadLanguage(languageId: string): Promise { + console.log(`Loading language ${languageId}`); + const parser = new Parser(); + const language = await Parser.Language.load( + `${this.wasmDirectory}/tree-sitter-${languageId}.wasm`, + ); + parser.setLanguage(language); + this.parsers.set(languageId, parser); + return true; + } + + getLanguage(languageId: string): Language | undefined { + console.log(`get language ${languageId}`); + return this.parsers.get(languageId)?.getLanguage(); + } + + getNodeAtLocation(document: TextDocument, range: Range): SyntaxNode { + console.log(`get node at ${document.languageId}`); + const tree = this.getTree(document); + const node = tree.rootNode.descendantForPosition( + { + row: range.start.line, + column: range.start.character, + }, + { + row: range.end.line, + column: range.end.character, + }, + ); + return node; + } +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts new file mode 100644 index 0000000000..abb61f0d60 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts @@ -0,0 +1,27 @@ +import { + Notifier, + type Disposable, + type RawTreeSitterQueryProvider, +} from "@cursorless/common"; +import type { JetbrainsIDE } from "./JetbrainsIDE"; + +export class JetbrainsTreeSitterQueryProvider + implements RawTreeSitterQueryProvider +{ + private notifier: Notifier = new Notifier(); + private disposables: Disposable[] = []; + + constructor(private ide: JetbrainsIDE) {} + + onChanges = this.notifier.registerListener; + + async readQuery(filename: string): Promise { + console.log("readQuery", filename); + const queryContents = await this.ide.readQuery(filename); + return queryContents; + } + + dispose() { + this.disposables.forEach((disposable) => disposable.dispose()); + } +} diff --git a/packages/cursorless-jetbrains/tsconfig.json b/packages/cursorless-jetbrains/tsconfig.json index 0ac9478318..8d9093f102 100644 --- a/packages/cursorless-jetbrains/tsconfig.json +++ b/packages/cursorless-jetbrains/tsconfig.json @@ -25,5 +25,5 @@ "path": "../test-case-recorder" } ], - "include": ["src/**/*.ts", "src/**/*.json", "../../typings/**/*.d.ts"] + "include": ["src/**/*.ts", "src/**/*.json", "../../typings/**/object.d.ts"] } From 1798e430cade30f24b4cf59e144c390790172c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Tue, 10 Dec 2024 07:55:32 +0100 Subject: [PATCH 17/34] Remove neovim references from jetbrains package --- packages/cursorless-jetbrains/TERMINOLOGY.md | 9 +++------ packages/cursorless-jetbrains/tsconfig.json | 6 ------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/cursorless-jetbrains/TERMINOLOGY.md b/packages/cursorless-jetbrains/TERMINOLOGY.md index a8fadf07cc..bcad13f7fd 100644 --- a/packages/cursorless-jetbrains/TERMINOLOGY.md +++ b/packages/cursorless-jetbrains/TERMINOLOGY.md @@ -1,7 +1,4 @@ -# TextEditor/TextDocument vs Window/Buffer +# TextEditor/TextDocument vs Project/Editor -1. Each Cursorless "TextDocument" corresponds to a neovim "Buffer" -2. Each Cursorless "TextEditor" corresponds to a neovim "Window" -3. A "TextEditor" corresponds to a view of a "TextDocument". The same "TextDocument" can be opened in two different "TextEditor". -4. When a "Window" changes in neovim, we need to reflect its "TextEditor" -5. When a "Buffer" changes in neovim, we need to reflect its "TextDocument". +1. Each Cursorless "TextDocument" corresponds to a Jetbrains "Editor" tab +2. There is no specific handling of projects diff --git a/packages/cursorless-jetbrains/tsconfig.json b/packages/cursorless-jetbrains/tsconfig.json index 8d9093f102..9b4b0c1914 100644 --- a/packages/cursorless-jetbrains/tsconfig.json +++ b/packages/cursorless-jetbrains/tsconfig.json @@ -12,12 +12,6 @@ { "path": "../cursorless-engine" }, - { - "path": "../neovim-common" - }, - { - "path": "../neovim-registry" - }, { "path": "../node-common" }, From 863103d290812d6aeea5a4459eb42ab2cdb1d846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Tue, 10 Dec 2024 08:02:47 +0100 Subject: [PATCH 18/34] update pnpm-lock.yaml --- pnpm-lock.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 623716e576..79d1e33e10 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -386,6 +386,12 @@ importers: '@cursorless/test-case-recorder': specifier: workspace:* version: link:../test-case-recorder + vscode-uri: + specifier: ^3.0.8 + version: 3.0.8 + web-tree-sitter: + specifier: 0.24.4 + version: 0.24.4 devDependencies: '@types/chai': specifier: ^5.0.0 @@ -9793,6 +9799,9 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-tree-sitter@0.24.4: + resolution: {integrity: sha512-sETP1Sf9OTd4LusrKBNznNgTt3fWoWhJnAFaKPiGSeVKXJbZ72qoMpxddKMdVI5BgXv32OI7tkKQre5PmF9reA==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -21430,6 +21439,8 @@ snapshots: web-namespaces@2.0.1: {} + web-tree-sitter@0.24.4: {} + webidl-conversions@3.0.1: {} webidl-conversions@7.0.0: {} From 8d3537291319ee8d674ea6094f990218995492e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Tue, 10 Dec 2024 10:53:25 +0100 Subject: [PATCH 19/34] fix various linting issues --- packages/cursorless-jetbrains/package.json | 7 ++----- .../src/ide/JetbrainsCommandServer.ts | 9 +-------- .../src/ide/JetbrainsConfiguration.ts | 1 - .../cursorless-jetbrains/src/ide/JetbrainsEvents.ts | 4 ++-- .../cursorless-jetbrains/src/ide/JetbrainsHats.ts | 2 +- .../cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 5 +---- .../src/ide/jetbrainsPerformEdits.ts | 1 - .../src/types/jetbrains.types.ts | 2 +- packages/cursorless-jetbrains/tsconfig.json | 7 +------ pnpm-lock.yaml | 12 ------------ tsconfig.json | 3 +++ 11 files changed, 12 insertions(+), 41 deletions(-) diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index a06aad6639..74eebe6919 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -4,6 +4,7 @@ "description": "cursorless in jetbrains", "main": "./out/cursorless.js", "private": true, + "type": "module", "scripts": { "build": "pnpm run esbuild:prod && pnpm run populate-dist", "compile": "tsc --build", @@ -35,10 +36,6 @@ "web-tree-sitter": "0.24.4" }, "devDependencies": { - "@types/chai": "^5.0.0", - "@types/js-yaml": "^4.0.9", - "@types/lodash": "4.17.10", - "@types/uuid": "^10.0.0", - "lodash": "^4.17.21" + "@types/chai": "^5.0.0" } } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts index 82bc2ac262..ed891a2d8a 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts @@ -1,8 +1 @@ -import type { CommandServerApi} from "@cursorless/common"; -import { JetbrainsClient } from "./JetbrainsClient"; - -export class JetbrainsCommandServer { - - - -} +export class JetbrainsCommandServer {} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts index a7633f17e8..2cdffe861b 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts @@ -1,4 +1,3 @@ -import { HatStability } from "@cursorless/common"; import { get } from "lodash"; import type { Configuration, diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts index 690d10858e..b478991ed3 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts @@ -7,13 +7,13 @@ import type { import { Position, Range } from "@cursorless/common"; export function jetbrainsOnDidChangeTextDocument( - listener: (event: TextDocumentChangeEvent) => void, + _listener: (event: TextDocumentChangeEvent) => void, ): Disposable { return dummyEvent(); } export function jetbrainsOnDidOpenTextDocument( - listener: (event: TextDocument) => any, + _listener: (event: TextDocument) => any, _thisArgs?: any, _disposables?: Disposable[] | undefined, ): Disposable { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index a1fab1384a..927c37ecb7 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -7,7 +7,7 @@ import type { } from "@cursorless/common"; import { Notifier } from "@cursorless/common"; import type { JetbrainsClient } from "./JetbrainsClient"; -import { JetbrainsHatRange } from "../types/jetbrains.types"; +import type { JetbrainsHatRange } from "../types/jetbrains.types"; const HAT_COLORS = [ "default", diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index c1a1ba6aed..448945b421 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -19,10 +19,7 @@ import type { } from "@cursorless/common"; import { pull } from "lodash"; import { JetbrainsCapabilities } from "./JetbrainsCapabilities"; -import { - fromJetbrainsContentChange, - jetbrainsOnDidOpenTextDocument, -} from "./JetbrainsEvents"; +import { fromJetbrainsContentChange } from "./JetbrainsEvents"; import type { JetbrainsClient } from "./JetbrainsClient"; import { JetbrainsClipboard } from "./JetbrainsClipboard"; diff --git a/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts b/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts index a031cf1525..7e7c6a6ab8 100644 --- a/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts +++ b/packages/cursorless-jetbrains/src/ide/jetbrainsPerformEdits.ts @@ -1,6 +1,5 @@ import type { Edit } from "@cursorless/common"; import { type InMemoryTextDocument } from "@cursorless/common"; -import type { Jetbrains } from "../types/jetbrains.types"; import type { EditorEdit } from "../types/types"; import type { JetbrainsIDE } from "./JetbrainsIDE"; import type { JetbrainsClient } from "./JetbrainsClient"; diff --git a/packages/cursorless-jetbrains/src/types/jetbrains.types.ts b/packages/cursorless-jetbrains/src/types/jetbrains.types.ts index e81eb391c4..648f9077e6 100644 --- a/packages/cursorless-jetbrains/src/types/jetbrains.types.ts +++ b/packages/cursorless-jetbrains/src/types/jetbrains.types.ts @@ -1,4 +1,4 @@ -import { Range } from "@cursorless/common"; +import type { Range } from "@cursorless/common"; import type { EditorEdit, EditorState, SelectionOffsets } from "./types"; export type JetbrainsNamespace = "user"; diff --git a/packages/cursorless-jetbrains/tsconfig.json b/packages/cursorless-jetbrains/tsconfig.json index 9b4b0c1914..3782b54e28 100644 --- a/packages/cursorless-jetbrains/tsconfig.json +++ b/packages/cursorless-jetbrains/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "target": "ES2020", - "rootDir": "src", - "outDir": "out" + "target": "ES2020" }, "references": [ { @@ -12,9 +10,6 @@ { "path": "../cursorless-engine" }, - { - "path": "../node-common" - }, { "path": "../test-case-recorder" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79d1e33e10..ece52e246a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -396,18 +396,6 @@ importers: '@types/chai': specifier: ^5.0.0 version: 5.0.0 - '@types/js-yaml': - specifier: ^4.0.9 - version: 4.0.9 - '@types/lodash': - specifier: 4.17.10 - version: 4.17.10 - '@types/uuid': - specifier: ^10.0.0 - version: 10.0.0 - lodash: - specifier: ^4.17.21 - version: 4.17.21 packages/cursorless-neovim: dependencies: diff --git a/tsconfig.json b/tsconfig.json index 20ea84c510..29f9d90f93 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,9 @@ { "path": "./packages/cursorless-everywhere-talon-e2e" }, + { + "path": "./packages/cursorless-jetbrains" + }, { "path": "./packages/cursorless-neovim" }, From 5a8e4a32afa8d1ef2f742d727bd3c69d94491351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Tue, 10 Dec 2024 12:52:32 +0100 Subject: [PATCH 20/34] fix more linting issues, and missing lodash dependency --- packages/cursorless-jetbrains/package.json | 4 +- .../src/ide/JetbrainsConfiguration.ts | 42 +------------------ .../src/ide/JetbrainsIDE.ts | 2 +- pnpm-lock.yaml | 6 +++ 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index 74eebe6919..9420936128 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -32,10 +32,12 @@ "@cursorless/common": "workspace:*", "@cursorless/cursorless-engine": "workspace:*", "@cursorless/test-case-recorder": "workspace:*", + "lodash-es": "^4.17.21", "vscode-uri": "^3.0.8", "web-tree-sitter": "0.24.4" }, "devDependencies": { - "@types/chai": "^5.0.0" + "@types/chai": "^5.0.0", + "@types/lodash-es": "4.17.12" } } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts index 2cdffe861b..3c8ebf6d72 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts @@ -1,4 +1,3 @@ -import { get } from "lodash"; import type { Configuration, ConfigurationScope, @@ -7,6 +6,7 @@ import type { import { CONFIGURATION_DEFAULTS } from "@cursorless/common"; import type { GetFieldType, Paths } from "@cursorless/common"; import { Notifier } from "@cursorless/common"; +import { get } from "lodash-es"; export class JetbrainsConfiguration implements Configuration { private notifier = new Notifier(); @@ -18,7 +18,7 @@ export class JetbrainsConfiguration implements Configuration { getOwnConfiguration>( path: Path, - scope?: ConfigurationScope, + _scope?: ConfigurationScope, ): GetFieldType { return get(this.configuration, path) as GetFieldType< CursorlessConfiguration, @@ -33,41 +33,3 @@ export class JetbrainsConfiguration implements Configuration { this.notifier.notifyListeners(); } } - -export function createJetbrainsConfiguration( - configuration: CursorlessConfiguration, -): JetbrainsConfiguration { - return new JetbrainsConfiguration(configuration); -} - -/** - * Gets a configuration value from jetbrains, with supported variables expanded. - * For example, `${userHome}` will be expanded to the user's home directory. - * - * We currently only support `${userHome}`. - * - * @param path The path to the configuration value, eg `cursorless.snippetsDir` - * @returns The configuration value, with variables expanded, or undefined if - * the value is not set - */ -export function jetbrainsGetConfigurationString( - path: string, -): string | undefined { - const index = path.lastIndexOf("."); - const section = path.substring(0, index); - const field = path.substring(index + 1); - // const value = "jetbrains.workspace.getConfiguration(section).get(field)"; - // return value != null ? evaluateStringVariables(value) : undefined; - return undefined; -} - -function evaluateStringVariables(value: string): string { - return value.replace(/\${(\w+)}/g, (match, variable) => { - switch (variable) { - case "userHome": - return "~/"; - default: - throw Error(`Unknown jetbrains configuration variable '${variable}'`); - } - }); -} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 448945b421..15bd80060c 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -17,7 +17,7 @@ import type { TextEditor, WorkspaceFolder, } from "@cursorless/common"; -import { pull } from "lodash"; +import { pull } from "lodash-es"; import { JetbrainsCapabilities } from "./JetbrainsCapabilities"; import { fromJetbrainsContentChange } from "./JetbrainsEvents"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ece52e246a..186e2557f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -386,6 +386,9 @@ importers: '@cursorless/test-case-recorder': specifier: workspace:* version: link:../test-case-recorder + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 vscode-uri: specifier: ^3.0.8 version: 3.0.8 @@ -396,6 +399,9 @@ importers: '@types/chai': specifier: ^5.0.0 version: 5.0.0 + '@types/lodash-es': + specifier: 4.17.12 + version: 4.17.12 packages/cursorless-neovim: dependencies: From e39d5b80eccdf902cec6e6187e5acfe55682b677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Tue, 10 Dec 2024 13:24:19 +0100 Subject: [PATCH 21/34] dont break build --- .../scripts/populate-dist.sh | 6 +++ .../scripts/test-quickjs.sh | 45 ------------------- 2 files changed, 6 insertions(+), 45 deletions(-) create mode 100755 packages/cursorless-jetbrains/scripts/populate-dist.sh delete mode 100755 packages/cursorless-jetbrains/scripts/test-quickjs.sh diff --git a/packages/cursorless-jetbrains/scripts/populate-dist.sh b/packages/cursorless-jetbrains/scripts/populate-dist.sh new file mode 100755 index 0000000000..a8c27b8990 --- /dev/null +++ b/packages/cursorless-jetbrains/scripts/populate-dist.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Populating dist directory..." + +echo "Nothing to do yet..." diff --git a/packages/cursorless-jetbrains/scripts/test-quickjs.sh b/packages/cursorless-jetbrains/scripts/test-quickjs.sh deleted file mode 100755 index 83125aa022..0000000000 --- a/packages/cursorless-jetbrains/scripts/test-quickjs.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -set -euo pipefail - -QUICKJS_VERSION=2024-01-13 - -echo $ cd out -cd out - -if [[ "$OSTYPE" == "darwin"* ]]; then - brew install quickjs - # Brew doesn't actually publish different versions of the quickjs binary - # brew install quickjs@$QUICKJS_VERSION - - echo $ qjs -I quickjsTest.mjs - qjs -I quickjsTest.mjs - - exit 0 -fi - -QUICKJS_URL_WIN=https://bellard.org/quickjs/binary_releases/quickjs-win-x86_64-$QUICKJS_VERSION.zip -QUICKJS_URL_LINUX=https://bellard.org/quickjs/binary_releases/quickjs-linux-x86_64-$QUICKJS_VERSION.zip -QUICKJS_FILE=quickjs.zip - -if [[ "$OSTYPE" == "linux-gnu"* ]]; then - QUICKJS_URL=$QUICKJS_URL_LINUX -elif [[ "$OSTYPE" == "cygwin" ]]; then - QUICKJS_URL=$QUICKJS_URL_WIN -elif [[ "$OSTYPE" == "msys" ]]; then - QUICKJS_URL=$QUICKJS_URL_WIN -elif [[ "$OSTYPE" == "win32" ]]; then - QUICKJS_URL=$QUICKJS_URL_WIN -else - echo "ERROR Unsupported OS: $OSTYPE" - exit 1 -fi - -echo $ curl -o $QUICKJS_FILE $QUICKJS_URL -curl -o $QUICKJS_FILE $QUICKJS_URL - -echo $ unzip $QUICKJS_FILE -unzip $QUICKJS_FILE - -echo $ ./qjs -I cursorless.js -./qjs --module cursorless.js From cd4c30a3ea52a792058679c75de886a42567a518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Fri, 13 Dec 2024 09:52:22 +0100 Subject: [PATCH 22/34] add path shim for cross os path parsing --- packages/cursorless-jetbrains/package.json | 4 ++- .../cursorless-jetbrains/src/extension.ts | 2 +- .../src/ide/JetbrainsConfiguration.ts | 7 +++++ .../src/ide/JetbrainsTreeSitter.ts | 8 +++-- pnpm-lock.yaml | 30 ++++++++++++++----- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index 9420936128..cbda3b1011 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -33,11 +33,13 @@ "@cursorless/cursorless-engine": "workspace:*", "@cursorless/test-case-recorder": "workspace:*", "lodash-es": "^4.17.21", + "path-browserify": "1.0.1", "vscode-uri": "^3.0.8", "web-tree-sitter": "0.24.4" }, "devDependencies": { "@types/chai": "^5.0.0", - "@types/lodash-es": "4.17.12" + "@types/lodash-es": "4.17.12", + "@types/path-browserify": "1.0.3" } } diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index 2b74f05f20..6b1fb45a61 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -9,7 +9,7 @@ export async function activate( plugin: JetbrainsPlugin, wasmDirectory: string, ): Promise { - console.log("activate started"); + console.log("activate started with wasm dir " + wasmDirectory); await Parser.init({ locateFile(scriptName: string, _scriptDirectory: string) { console.log("locateFile called for " + scriptName); diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts index 3c8ebf6d72..803aaa09b4 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsConfiguration.ts @@ -33,3 +33,10 @@ export class JetbrainsConfiguration implements Configuration { this.notifier.notifyListeners(); } } + +export function createJetbrainsConfiguration( + configuration: CursorlessConfiguration, +): JetbrainsConfiguration { + return new JetbrainsConfiguration(configuration); +} + diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts index 89c089ae86..3795354de4 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts @@ -1,6 +1,7 @@ import type { Range, TextDocument, TreeSitter } from "@cursorless/common"; import type { Language, SyntaxNode, Tree } from "web-tree-sitter"; import Parser from "web-tree-sitter"; +import path from "path-browserify"; export class JetbrainsTreeSitter implements TreeSitter { constructor(private wasmDirectory: string) {} @@ -21,9 +22,10 @@ export class JetbrainsTreeSitter implements TreeSitter { async loadLanguage(languageId: string): Promise { console.log(`Loading language ${languageId}`); const parser = new Parser(); - const language = await Parser.Language.load( - `${this.wasmDirectory}/tree-sitter-${languageId}.wasm`, - ); + const dir = path.parse(this.wasmDirectory).dir; + const filePath = path.join(dir, `tree-sitter-${languageId}.wasm`); + console.log(`Loading language from ${filePath}`); + const language = await Parser.Language.load(filePath); parser.setLanguage(language); this.parsers.set(languageId, parser); return true; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 186e2557f8..0b33e173ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ importers: version: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + version: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-mocha: specifier: 10.5.0 version: 10.5.0(eslint@8.57.1) @@ -389,6 +389,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.17.21 + path-browserify: + specifier: 1.0.1 + version: 1.0.1 vscode-uri: specifier: ^3.0.8 version: 3.0.8 @@ -402,6 +405,9 @@ importers: '@types/lodash-es': specifier: 4.17.12 version: 4.17.12 + '@types/path-browserify': + specifier: 1.0.3 + version: 1.0.3 packages/cursorless-neovim: dependencies: @@ -3204,6 +3210,9 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/path-browserify@1.0.3': + resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==} + '@types/prismjs@1.26.4': resolution: {integrity: sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==} @@ -7702,6 +7711,9 @@ packages: resolution: {integrity: sha512-gds5iRhSeOcDtj8gfWkRHLtZKTPsFVuh7utbjYtvnclw4XM+ffRzJrwqMhOD1PVqef7nBLmgsu1vIujjvAJrAw==} engines: {node: '>=4'} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -13399,6 +13411,8 @@ snapshots: '@types/parse-json@4.0.2': {} + '@types/path-browserify@1.0.3': {} + '@types/prismjs@1.26.4': {} '@types/prop-types@15.7.13': {} @@ -15464,7 +15478,7 @@ snapshots: eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1) eslint-plugin-react: 7.37.1(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -15493,20 +15507,20 @@ snapshots: debug: 4.3.7(supports-color@8.1.1) enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -15517,7 +15531,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -15528,7 +15542,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -19169,6 +19183,8 @@ snapshots: path-absolute@1.0.1: {} + path-browserify@1.0.1: {} + path-exists@3.0.0: {} path-exists@4.0.0: {} From d10638aa110c52a3edffae579fe3f76d97dcc730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Fri, 13 Dec 2024 23:28:25 +0100 Subject: [PATCH 23/34] fix windows was path building --- packages/cursorless-jetbrains/package.json | 4 +-- .../cursorless-jetbrains/src/extension.ts | 7 ++-- .../src/ide/JetbrainsTreeSitter.ts | 12 +++---- pnpm-lock.yaml | 34 +++++-------------- 4 files changed, 19 insertions(+), 38 deletions(-) diff --git a/packages/cursorless-jetbrains/package.json b/packages/cursorless-jetbrains/package.json index cbda3b1011..9420936128 100644 --- a/packages/cursorless-jetbrains/package.json +++ b/packages/cursorless-jetbrains/package.json @@ -33,13 +33,11 @@ "@cursorless/cursorless-engine": "workspace:*", "@cursorless/test-case-recorder": "workspace:*", "lodash-es": "^4.17.21", - "path-browserify": "1.0.1", "vscode-uri": "^3.0.8", "web-tree-sitter": "0.24.4" }, "devDependencies": { "@types/chai": "^5.0.0", - "@types/lodash-es": "4.17.12", - "@types/path-browserify": "1.0.3" + "@types/lodash-es": "4.17.12" } } diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index 6b1fb45a61..189e2db1f9 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -4,16 +4,17 @@ import type { JetbrainsPlugin } from "./ide/JetbrainsPlugin"; import Parser from "web-tree-sitter"; import { JetbrainsTreeSitter } from "./ide/JetbrainsTreeSitter"; import { JetbrainsTreeSitterQueryProvider } from "./ide/JetbrainsTreeSitterQueryProvider"; +import { pathJoin } from "./ide/pathJoin"; export async function activate( plugin: JetbrainsPlugin, wasmDirectory: string, ): Promise { - console.log("activate started with wasm dir " + wasmDirectory); + console.log("activate started"); await Parser.init({ locateFile(scriptName: string, _scriptDirectory: string) { - console.log("locateFile called for " + scriptName); - return wasmDirectory + scriptName; + const fullPath = pathJoin(wasmDirectory, scriptName); + return fullPath; }, }); console.log("Parser initialized"); diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts index 3795354de4..75d3fe8035 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts @@ -1,7 +1,7 @@ import type { Range, TextDocument, TreeSitter } from "@cursorless/common"; import type { Language, SyntaxNode, Tree } from "web-tree-sitter"; import Parser from "web-tree-sitter"; -import path from "path-browserify"; +import { pathJoin } from "./pathJoin"; export class JetbrainsTreeSitter implements TreeSitter { constructor(private wasmDirectory: string) {} @@ -9,7 +9,6 @@ export class JetbrainsTreeSitter implements TreeSitter { parsers = new Map(); getTree(document: TextDocument): Tree { - console.log(`get tree ${document.languageId}`); if (this.getLanguage(document.languageId)) { const parser = this.parsers.get(document.languageId); if (parser) { @@ -22,9 +21,10 @@ export class JetbrainsTreeSitter implements TreeSitter { async loadLanguage(languageId: string): Promise { console.log(`Loading language ${languageId}`); const parser = new Parser(); - const dir = path.parse(this.wasmDirectory).dir; - const filePath = path.join(dir, `tree-sitter-${languageId}.wasm`); - console.log(`Loading language from ${filePath}`); + const filePath = pathJoin( + this.wasmDirectory, + `tree-sitter-${languageId}.wasm`, + ); const language = await Parser.Language.load(filePath); parser.setLanguage(language); this.parsers.set(languageId, parser); @@ -32,12 +32,10 @@ export class JetbrainsTreeSitter implements TreeSitter { } getLanguage(languageId: string): Language | undefined { - console.log(`get language ${languageId}`); return this.parsers.get(languageId)?.getLanguage(); } getNodeAtLocation(document: TextDocument, range: Range): SyntaxNode { - console.log(`get node at ${document.languageId}`); const tree = this.getTree(document); const node = tree.rootNode.descendantForPosition( { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b33e173ef..49fcc12cce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ importers: version: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + version: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) eslint-plugin-mocha: specifier: 10.5.0 version: 10.5.0(eslint@8.57.1) @@ -118,7 +118,7 @@ importers: version: 29.7.0 ts-jest: specifier: 29.2.5 - version: 29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.16.0)(ts-node@10.9.2(@types/node@20.16.0)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.16.0))(typescript@5.6.3) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -389,9 +389,6 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.17.21 - path-browserify: - specifier: 1.0.1 - version: 1.0.1 vscode-uri: specifier: ^3.0.8 version: 3.0.8 @@ -405,9 +402,6 @@ importers: '@types/lodash-es': specifier: 4.17.12 version: 4.17.12 - '@types/path-browserify': - specifier: 1.0.3 - version: 1.0.3 packages/cursorless-neovim: dependencies: @@ -3210,9 +3204,6 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - '@types/path-browserify@1.0.3': - resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==} - '@types/prismjs@1.26.4': resolution: {integrity: sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==} @@ -7711,9 +7702,6 @@ packages: resolution: {integrity: sha512-gds5iRhSeOcDtj8gfWkRHLtZKTPsFVuh7utbjYtvnclw4XM+ffRzJrwqMhOD1PVqef7nBLmgsu1vIujjvAJrAw==} engines: {node: '>=4'} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -13411,8 +13399,6 @@ snapshots: '@types/parse-json@4.0.2': {} - '@types/path-browserify@1.0.3': {} - '@types/prismjs@1.26.4': {} '@types/prop-types@15.7.13': {} @@ -15478,7 +15464,7 @@ snapshots: eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1) eslint-plugin-react: 7.37.1(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -15507,20 +15493,20 @@ snapshots: debug: 4.3.7(supports-color@8.1.1) enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -15531,7 +15517,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -15542,7 +15528,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -19183,8 +19169,6 @@ snapshots: path-absolute@1.0.1: {} - path-browserify@1.0.1: {} - path-exists@3.0.0: {} path-exists@4.0.0: {} @@ -21046,7 +21030,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.16.0)(ts-node@10.9.2(@types/node@20.16.0)(typescript@5.6.3)))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.16.0))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 From f6085bafc7cd956fdf507b421bbbbdd8f3833aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sun, 15 Dec 2024 09:49:35 +0100 Subject: [PATCH 24/34] add missing pathjoin file --- packages/cursorless-jetbrains/src/ide/pathJoin.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/cursorless-jetbrains/src/ide/pathJoin.ts diff --git a/packages/cursorless-jetbrains/src/ide/pathJoin.ts b/packages/cursorless-jetbrains/src/ide/pathJoin.ts new file mode 100644 index 0000000000..4dc6e56151 --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/pathJoin.ts @@ -0,0 +1,11 @@ +export function pathJoin(...segments: string[]): string { + return segments.join(pathSep()); +} + +function pathSep() { + if (/^win/i.test(process.platform)) { + return "\\"; + } else { + return "/"; + } +} From 31c678b9acac3d14f025821e4b7211fe8ba8281b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Mon, 16 Dec 2024 21:36:50 +0100 Subject: [PATCH 25/34] support editor visibility --- .../cursorless-jetbrains/src/ide/JetbrainsEditor.ts | 1 + .../cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 12 ++++-------- packages/cursorless-jetbrains/src/types/types.ts | 1 + 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index 8089ca320a..e920f87a32 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -25,6 +25,7 @@ export class JetbrainsEditor implements EditableTextEditor { }; isActive = true; + isVisible = true; constructor( private client: JetbrainsClient, diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 15bd80060c..5879d06859 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -109,7 +109,7 @@ export class JetbrainsIDE implements IDE { get workspaceFolders(): readonly WorkspaceFolder[] | undefined { console.log("get workspaceFolders"); - throw new Error("workspaceFolders not implemented."); + throw new Error("workspaceFolders not get implemented."); } get activeTextEditor(): TextEditor | undefined { @@ -124,13 +124,7 @@ export class JetbrainsIDE implements IDE { get visibleTextEditors(): TextEditor[] { // console.log("get visibleTextEditors"); - //return [...this.editors.values()].filter((editor) => editor.isActive); - if (this.activeEditor) { - console.log("visible: " + this.activeEditor.id); - return [this.activeEditor]; - } else { - return []; - } + return [...this.editors.values()].filter((editor) => editor.isVisible); } getEditableTextEditor(editor: TextEditor): EditableTextEditor { @@ -293,6 +287,8 @@ function updateEditor(editor: JetbrainsEditor, editorState: EditorState) { editor.selections = editorState.selections.map((selection) => createSelection(editor.document, selection), ); + editor.isActive = editorState.active; + editor.isVisible = editorState.visible; } function getLines(text: string, firstLine: number, lastLine: number) { diff --git a/packages/cursorless-jetbrains/src/types/types.ts b/packages/cursorless-jetbrains/src/types/types.ts index 99ffd9f490..fb0e355c49 100644 --- a/packages/cursorless-jetbrains/src/types/types.ts +++ b/packages/cursorless-jetbrains/src/types/types.ts @@ -25,6 +25,7 @@ export interface EditorState { lastVisibleLine: number; selections: JbSelection[]; active: boolean; + visible: boolean; } export interface EditorChange { From bdf7fdca5c4c9fcd1ebd787192bc6c67c54197ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 18 Dec 2024 08:53:19 +0100 Subject: [PATCH 26/34] implement flashranges for jetbrains --- .../src/ide/JetbrainsClient.ts | 1 + .../src/ide/JetbrainsFlashDescriptor.ts | 7 +++++++ .../cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 packages/cursorless-jetbrains/src/ide/JetbrainsFlashDescriptor.ts diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index d08859953a..1e38756c29 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -9,4 +9,5 @@ export interface JetbrainsClient { insertLineAfter(editorId: string, rangesJson: string): void; revealLine(editorId: string, line: number, revealAt: string): void; readQuery(filename: string): string | undefined; + flashRanges(flashRangesJson: string): string | undefined; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsFlashDescriptor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsFlashDescriptor.ts new file mode 100644 index 0000000000..065e927b8b --- /dev/null +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsFlashDescriptor.ts @@ -0,0 +1,7 @@ +import type { GeneralizedRange } from "@cursorless/common"; + +export interface JetbrainsFlashDescriptor { + style: string; + editorId: string; + range: GeneralizedRange; +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 5879d06859..7dfb264e49 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -29,6 +29,9 @@ import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore"; import type { EditorState } from "../types/types"; import { createSelection, createTextEditor } from "./createTextEditor"; import { JetbrainsEditor } from "./JetbrainsEditor"; +import { CharacterRange } from "../../../common/out/types/GeneralizedRange"; +import { ide } from "@cursorless/cursorless-engine"; +import { JetbrainsFlashDescriptor } from "./JetbrainsFlashDescriptor"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; @@ -93,8 +96,17 @@ export class JetbrainsIDE implements IDE { throw Error("setHighlightRanges Not implemented"); } - async flashRanges(_flashDescriptors: FlashDescriptor[]): Promise { - console.debug("flashRanges Not implemented"); + async flashRanges(flashDescriptors: FlashDescriptor[]): Promise { + console.log("flashRangeses"); + const jbfs = flashDescriptors.map((flashDescriptor) => { + const jbf: JetbrainsFlashDescriptor = { + editorId: flashDescriptor.editor.id, + range: flashDescriptor.range, + style: flashDescriptor.style, + }; + return jbf; + }); + this.client.flashRanges(JSON.stringify(jbfs)); } get assetsRoot(): string { From d8bbe845b595c0c1efc413661cbb3cf9af03462d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Wed, 18 Dec 2024 17:17:00 +0100 Subject: [PATCH 27/34] add support for prePhraseVersion --- .../cursorless-jetbrains/src/extension.ts | 3 +++ .../src/ide/JetbrainsClient.ts | 1 + .../src/ide/JetbrainsCommandServer.ts | 26 ++++++++++++++++++- .../src/ide/JetbrainsIDE.ts | 4 +-- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-jetbrains/src/extension.ts b/packages/cursorless-jetbrains/src/extension.ts index 189e2db1f9..fab1f08f10 100644 --- a/packages/cursorless-jetbrains/src/extension.ts +++ b/packages/cursorless-jetbrains/src/extension.ts @@ -5,6 +5,7 @@ import Parser from "web-tree-sitter"; import { JetbrainsTreeSitter } from "./ide/JetbrainsTreeSitter"; import { JetbrainsTreeSitterQueryProvider } from "./ide/JetbrainsTreeSitterQueryProvider"; import { pathJoin } from "./ide/pathJoin"; +import { JetbrainsCommandServer } from "./ide/JetbrainsCommandServer"; export async function activate( plugin: JetbrainsPlugin, @@ -19,12 +20,14 @@ export async function activate( }); console.log("Parser initialized"); + const commandServerApi = new JetbrainsCommandServer(plugin.client); const queryProvider = new JetbrainsTreeSitterQueryProvider(plugin.ide); const engine = await createCursorlessEngine({ ide: plugin.ide, hats: plugin.hats, treeSitterQueryProvider: queryProvider, treeSitter: new JetbrainsTreeSitter(wasmDirectory), + commandServerApi: commandServerApi, }); console.log("activate completed"); return engine; diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts index 1e38756c29..1ac027e3b3 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsClient.ts @@ -1,4 +1,5 @@ export interface JetbrainsClient { + prePhraseVersion(): string | PromiseLike | null; clipboardCopy(editorId: string, rangesJson: string): void; clipboardPaste(editorId: string): void; hatsUpdated(hatsJson: string): void; diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts index ed891a2d8a..af97926609 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsCommandServer.ts @@ -1 +1,25 @@ -export class JetbrainsCommandServer {} +import type { JetbrainsClient } from "./JetbrainsClient"; +import type { + CommandServerApi, + FocusedElementType, + InboundSignal, +} from "@cursorless/common"; + +export class JetbrainsCommandServer implements CommandServerApi { + private client!: JetbrainsClient; + readonly signals: { prePhrase: InboundSignal } = { + prePhrase: { + getVersion: async () => { + return this.client.prePhraseVersion(); + }, + }, + }; + + constructor(client: JetbrainsClient) { + this.client = client; + } + + getFocusedElementType(): Promise { + return Promise.resolve(undefined); + } +} diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 7dfb264e49..16de7d7d76 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -29,9 +29,7 @@ import { JetbrainsKeyValueStore } from "./JetbrainsKeyValueStore"; import type { EditorState } from "../types/types"; import { createSelection, createTextEditor } from "./createTextEditor"; import { JetbrainsEditor } from "./JetbrainsEditor"; -import { CharacterRange } from "../../../common/out/types/GeneralizedRange"; -import { ide } from "@cursorless/cursorless-engine"; -import { JetbrainsFlashDescriptor } from "./JetbrainsFlashDescriptor"; +import type { JetbrainsFlashDescriptor } from "./JetbrainsFlashDescriptor"; export class JetbrainsIDE implements IDE { readonly configuration: JetbrainsConfiguration; From 97218fa115e92f873c86ff6b47eca8c280b8d7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sun, 22 Dec 2024 15:02:22 +0100 Subject: [PATCH 28/34] enable multiple hat styles --- .../src/ide/JetbrainsHats.ts | 43 ++++++++++++++----- .../src/ide/JetbrainsIDE.ts | 7 ++- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index 927c37ecb7..b119129d30 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -2,6 +2,7 @@ import type { Disposable, HatRange, Hats, + HatStyleInfo, HatStyleMap, Listener, } from "@cursorless/common"; @@ -20,11 +21,16 @@ const HAT_COLORS = [ export class JetbrainsHats implements Hats { private isEnabledNotifier: Notifier<[boolean]> = new Notifier(); + private hatStyleChangedNotifier: Notifier<[HatStyleMap]> = new Notifier(); + private hatRanges: HatRange[] = []; private client: JetbrainsClient; + enabledHatStyles: HatStyleMap; + private enabledHatShapes = ["default"]; constructor(client: JetbrainsClient) { this.client = client; + this.enabledHatStyles = this.generateHatStyles(); } setHatRanges(hatRanges: HatRange[]): Promise { @@ -38,6 +44,12 @@ export class JetbrainsHats implements Hats { return Promise.resolve(); } + setEnabledHatShapes(enabledHatShapes: string[]): void { + this.enabledHatShapes = enabledHatShapes; + this.enabledHatStyles = this.generateHatStyles(); + this.hatStyleChangedNotifier.notifyListeners(this.enabledHatStyles); + } + toJetbransHatRanges(hatRanges: HatRange[]): JetbrainsHatRange[] { return hatRanges.map((range) => { return { @@ -48,20 +60,31 @@ export class JetbrainsHats implements Hats { }); } - enabledHatStyles: HatStyleMap = Object.fromEntries( - HAT_COLORS.map((color) => [ - color, - { penalty: color === "default" ? 0 : 1 }, - ]), - ); + private generateHatStyles(): HatStyleMap { + const res = new Map(); + for (const color of HAT_COLORS) { + const colorPenalty = color === "default" ? 0 : 1; + for (const shape of this.enabledHatShapes) { + const shapePenalty = shape === "default" ? 0 : 2; + let styleName: string; + if (shape === "default") { + styleName = color; + } else { + styleName = `${color}-${shape}`; + } + res.set(styleName, { penalty: colorPenalty + shapePenalty }); + } + } + return Object.fromEntries(res); + } - onDidChangeEnabledHatStyles(_listener: Listener<[HatStyleMap]>): Disposable { - return { dispose: () => {} }; + onDidChangeEnabledHatStyles(listener: Listener<[HatStyleMap]>): Disposable { + return this.hatStyleChangedNotifier.registerListener(listener); } isEnabled: boolean = true; - onDidChangeIsEnabled(_listener: Listener<[boolean]>): Disposable { - return { dispose: () => {} }; + onDidChangeIsEnabled(listener: Listener<[boolean]>): Disposable { + return this.isEnabledNotifier.registerListener(listener); } toggle(isEnabled?: boolean) { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 16de7d7d76..7d6663ce7b 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -292,7 +292,12 @@ function updateEditor(editor: JetbrainsEditor, editorState: EditorState) { editorState.text, ); editor.visibleRanges = [ - new Range(editorState.firstVisibleLine, 0, editorState.lastVisibleLine, 0), + new Range( + editorState.firstVisibleLine, + 0, + editorState.lastVisibleLine + 1, + 0, + ), ]; editor.selections = editorState.selections.map((selection) => createSelection(editor.document, selection), From cfe93f927ebb37827122e9d4cf0c67227d06af25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sat, 28 Dec 2024 21:51:11 +0100 Subject: [PATCH 29/34] inject colors and shape settings from ide --- .../src/ide/JetbrainsHats.ts | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index b119129d30..9b29a4e454 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -27,6 +27,9 @@ export class JetbrainsHats implements Hats { private client: JetbrainsClient; enabledHatStyles: HatStyleMap; private enabledHatShapes = ["default"]; + private hatShapePenalties: Map = new Map([["default", 0]]); + private enabledHatColors = ["default"]; + private hatColorPenalties: Map = new Map([["default", 0]]); constructor(client: JetbrainsClient) { this.client = client; @@ -50,6 +53,30 @@ export class JetbrainsHats implements Hats { this.hatStyleChangedNotifier.notifyListeners(this.enabledHatStyles); } + setHatShapePenalties(hatShapePenalties: Map): void { + // supplied map is a json map, and not a typescript map, so convert it to typed map + this.hatShapePenalties = new Map( + Object.entries(hatShapePenalties), + ); + this.enabledHatStyles = this.generateHatStyles(); + this.hatStyleChangedNotifier.notifyListeners(this.enabledHatStyles); + } + + setEnabledHatColors(enabledHatColors: string[]): void { + this.enabledHatColors = enabledHatColors; + this.enabledHatStyles = this.generateHatStyles(); + this.hatStyleChangedNotifier.notifyListeners(this.enabledHatStyles); + } + + setHatColorPenalties(hatColorPenalties: Map): void { + // supplied map is a json map, and not a typescript map, so convert it to typed map + this.hatColorPenalties = new Map( + Object.entries(hatColorPenalties), + ); + this.enabledHatStyles = this.generateHatStyles(); + this.hatStyleChangedNotifier.notifyListeners(this.enabledHatStyles); + } + toJetbransHatRanges(hatRanges: HatRange[]): JetbrainsHatRange[] { return hatRanges.map((range) => { return { @@ -62,10 +89,10 @@ export class JetbrainsHats implements Hats { private generateHatStyles(): HatStyleMap { const res = new Map(); - for (const color of HAT_COLORS) { - const colorPenalty = color === "default" ? 0 : 1; + for (const color of this.enabledHatColors) { + const colorPenalty = this.getColorPenalty(color); for (const shape of this.enabledHatShapes) { - const shapePenalty = shape === "default" ? 0 : 2; + const shapePenalty = this.getShapePenalty(shape); let styleName: string; if (shape === "default") { styleName = color; @@ -78,6 +105,24 @@ export class JetbrainsHats implements Hats { return Object.fromEntries(res); } + private getShapePenalty(shape: string) { + let shapePenalty = this.hatShapePenalties.get(shape); + if (shapePenalty == null) { + shapePenalty = shape === "default" ? 0 : 2; + } else { + shapePenalty = shape === "default" ? shapePenalty : shapePenalty + 1; + } + return shapePenalty; + } + + private getColorPenalty(color: string) { + let colorPenalty = this.hatColorPenalties.get(color); + if (colorPenalty == null) { + colorPenalty = color === "default" ? 0 : 1; + } + return colorPenalty; + } + onDidChangeEnabledHatStyles(listener: Listener<[HatStyleMap]>): Disposable { return this.hatStyleChangedNotifier.registerListener(listener); } From 6370683c44cfade5f54f952e4b0513e915b71b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Thu, 9 Jan 2025 07:30:12 +0100 Subject: [PATCH 30/34] jetbrains: use "go to declaration" for "follow" command --- packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts | 2 +- packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index e920f87a32..8119e0e5f3 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -186,7 +186,7 @@ export class JetbrainsEditor implements EditableTextEditor { range ? [range] : [], true, false, - "QuickImplementations", + "GotoDeclaration", ); await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index 9b29a4e454..a3df17c819 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -10,15 +10,6 @@ import { Notifier } from "@cursorless/common"; import type { JetbrainsClient } from "./JetbrainsClient"; import type { JetbrainsHatRange } from "../types/jetbrains.types"; -const HAT_COLORS = [ - "default", - "blue", - "green", - "red", - "pink", - "yellow", -] as const; - export class JetbrainsHats implements Hats { private isEnabledNotifier: Notifier<[boolean]> = new Notifier(); private hatStyleChangedNotifier: Notifier<[HatStyleMap]> = new Notifier(); From 43ba5ac38b9368458ab597322a08c99034fb1625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Thu, 9 Jan 2025 08:30:00 +0100 Subject: [PATCH 31/34] improve editable editor support - add editable state --- .../src/ide/JetbrainsEditor.ts | 1 + .../cursorless-jetbrains/src/ide/JetbrainsHats.ts | 2 +- .../cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 15 ++++++++++----- packages/cursorless-jetbrains/src/types/types.ts | 1 + 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index 8119e0e5f3..f23c3c2813 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -26,6 +26,7 @@ export class JetbrainsEditor implements EditableTextEditor { isActive = true; isVisible = true; + isEditable = true; constructor( private client: JetbrainsClient, diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index a3df17c819..b9af510e90 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -21,6 +21,7 @@ export class JetbrainsHats implements Hats { private hatShapePenalties: Map = new Map([["default", 0]]); private enabledHatColors = ["default"]; private hatColorPenalties: Map = new Map([["default", 0]]); + isEnabled: boolean = true; constructor(client: JetbrainsClient) { this.client = client; @@ -118,7 +119,6 @@ export class JetbrainsHats implements Hats { return this.hatStyleChangedNotifier.registerListener(listener); } - isEnabled: boolean = true; onDidChangeIsEnabled(listener: Listener<[boolean]>): Disposable { return this.isEnabledNotifier.registerListener(listener); } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 7d6663ce7b..4d30043354 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -124,12 +124,12 @@ export class JetbrainsIDE implements IDE { get activeTextEditor(): TextEditor | undefined { // console.log("get activeTextEditor"); - return this.activeEditableTextEditor; + return this.activeEditor; } get activeEditableTextEditor(): EditableTextEditor | undefined { // console.log("get activeEditableTextEditor"); - return this.activeEditor; + return this.activeEditor?.isEditable ? this.activeEditor : undefined; } get visibleTextEditors(): TextEditor[] { @@ -137,11 +137,15 @@ export class JetbrainsIDE implements IDE { return [...this.editors.values()].filter((editor) => editor.isVisible); } - getEditableTextEditor(editor: TextEditor): EditableTextEditor { - // console.log("getEditableTextEditor"); + getEditableTextEditor(editor: TextEditor): EditableTextEditor { + console.log("getEditableTextEditor"); if (editor instanceof JetbrainsEditor) { console.log("getEditableTextEditor - return current"); - return editor; + if (editor.isEditable) { + return editor; + } else { + throw Error(`Editor is not editable: ${editor}`); + } } throw Error(`Unsupported text editor type: ${editor}`); } @@ -304,6 +308,7 @@ function updateEditor(editor: JetbrainsEditor, editorState: EditorState) { ); editor.isActive = editorState.active; editor.isVisible = editorState.visible; + editor.isEditable = editorState.editable; } function getLines(text: string, firstLine: number, lastLine: number) { diff --git a/packages/cursorless-jetbrains/src/types/types.ts b/packages/cursorless-jetbrains/src/types/types.ts index fb0e355c49..b8a4ba44fe 100644 --- a/packages/cursorless-jetbrains/src/types/types.ts +++ b/packages/cursorless-jetbrains/src/types/types.ts @@ -26,6 +26,7 @@ export interface EditorState { selections: JbSelection[]; active: boolean; visible: boolean; + editable: boolean; } export interface EditorChange { From d1334f275804f229ca17b1ec1cd3088e72bb9744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Fri, 17 Jan 2025 21:32:50 +0100 Subject: [PATCH 32/34] add cursorless tag for jetbrains app --- cursorless-talon/src/apps/cursorless_jetbrains.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 cursorless-talon/src/apps/cursorless_jetbrains.py diff --git a/cursorless-talon/src/apps/cursorless_jetbrains.py b/cursorless-talon/src/apps/cursorless_jetbrains.py new file mode 100644 index 0000000000..fe5ec8727d --- /dev/null +++ b/cursorless-talon/src/apps/cursorless_jetbrains.py @@ -0,0 +1,9 @@ +from talon import Context, actions + +ctx = Context() + +ctx.matches = r""" +app: jetbrains +""" + +ctx.tags = ["user.cursorless"] From f32055d9a8d978b423a0bfbd661485ff224458ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sun, 26 Jan 2025 14:40:31 +0100 Subject: [PATCH 33/34] update to newest cursorless reduce logging. --- .../cursorless-jetbrains/src/ide/JetbrainsEditor.ts | 6 +++--- .../cursorless-jetbrains/src/ide/JetbrainsEvents.ts | 12 ++++++------ .../cursorless-jetbrains/src/ide/JetbrainsHats.ts | 2 +- .../cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 6 ++++-- .../src/ide/JetbrainsTreeSitter.ts | 2 +- .../src/ide/JetbrainsTreeSitterQueryProvider.ts | 2 +- .../cursorless-jetbrains/src/ide/createTextEditor.ts | 4 ++-- .../cursorless-jetbrains/src/ide/setSelections.ts | 4 ++-- .../cursorless-jetbrains/src/settimeout-polyfill.ts | 5 +++++ 9 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 packages/cursorless-jetbrains/src/settimeout-polyfill.ts diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts index f23c3c2813..580cfe541b 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEditor.ts @@ -45,7 +45,7 @@ export class JetbrainsEditor implements EditableTextEditor { selections: Selection[], _opts?: SetSelectionsOpts, ): Promise { - console.log("editor.setSelections"); + // console.log("editor.setSelections"); if (!selectionsEqual(this.selections, selections)) { await setSelections(this.client, this.document, this.id, selections); this.selections = selections; @@ -53,7 +53,7 @@ export class JetbrainsEditor implements EditableTextEditor { } edit(edits: Edit[]): Promise { - console.log("editor.edit"); + // console.log("editor.edit"); jetbrainsPerformEdits(this.client, this.ide, this.document, this.id, edits); return Promise.resolve(true); } @@ -220,7 +220,7 @@ export class JetbrainsEditor implements EditableTextEditor { await this.client.executeRangeCommand(this.id, JSON.stringify(command)); } - editNewNotebookCellAbove(): Promise<(_selection: Selection) => Selection> { + editNewNotebookCellAbove(): Promise { throw new Error("editNewNotebookCellAbove not implemented."); } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts index b478991ed3..78290ddf8c 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsEvents.ts @@ -28,9 +28,9 @@ export function fromJetbrainsContentChange( ): TextDocumentContentChangeEvent[] { const result = []; const text = linedata.join("\n"); - console.debug( - `fromJetbrainsContentChange(): document.getText(): '${document.getText()}'`, - ); + // console.debug( + // `fromJetbrainsContentChange(): document.getText(): '${document.getText()}'`, + // ); const range = new Range( new Position(firstLine, 0), new Position(lastLine - 1, document.lineAt(lastLine - 1).text.length), @@ -43,9 +43,9 @@ export function fromJetbrainsContentChange( rangeLength: rangeLength, text: text, }); - console.debug( - `fromJetbrainsContentChange(): changes=${JSON.stringify(result)}`, - ); + // console.debug( + // `fromJetbrainsContentChange(): changes=${JSON.stringify(result)}`, + // ); return result; } diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts index b9af510e90..d39a0112e7 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsHats.ts @@ -29,7 +29,7 @@ export class JetbrainsHats implements Hats { } setHatRanges(hatRanges: HatRange[]): Promise { - console.log("ASOEE/CL: JetbrainsHats.setHatRanges : " + hatRanges.length); + // console.log("ASOEE/CL: JetbrainsHats.setHatRanges : " + hatRanges.length); this.hatRanges = hatRanges; const jbHatRanges = this.toJetbransHatRanges(hatRanges); diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 4d30043354..1934f876e7 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -16,6 +16,7 @@ import type { TextDocumentChangeEvent, TextEditor, WorkspaceFolder, + NotebookEditor, } from "@cursorless/common"; import { pull } from "lodash-es"; import { JetbrainsCapabilities } from "./JetbrainsCapabilities"; @@ -38,6 +39,7 @@ export class JetbrainsIDE implements IDE { readonly clipboard: JetbrainsClipboard; readonly capabilities: JetbrainsCapabilities; readonly runMode: RunMode = "development"; + readonly visibleNotebookEditors: NotebookEditor[] = []; // private editorMap; // private documentMap; private activeProject: Window | undefined; @@ -138,9 +140,9 @@ export class JetbrainsIDE implements IDE { } getEditableTextEditor(editor: TextEditor): EditableTextEditor { - console.log("getEditableTextEditor"); + // console.log("getEditableTextEditor"); if (editor instanceof JetbrainsEditor) { - console.log("getEditableTextEditor - return current"); + // console.log("getEditableTextEditor - return current"); if (editor.isEditable) { return editor; } else { diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts index 75d3fe8035..b1ecedf226 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitter.ts @@ -19,7 +19,7 @@ export class JetbrainsTreeSitter implements TreeSitter { } async loadLanguage(languageId: string): Promise { - console.log(`Loading language ${languageId}`); + // console.log(`Loading language ${languageId}`); const parser = new Parser(); const filePath = pathJoin( this.wasmDirectory, diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts index abb61f0d60..54b98484cf 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsTreeSitterQueryProvider.ts @@ -16,7 +16,7 @@ export class JetbrainsTreeSitterQueryProvider onChanges = this.notifier.registerListener; async readQuery(filename: string): Promise { - console.log("readQuery", filename); + // console.log("readQuery", filename); const queryContents = await this.ide.readQuery(filename); return queryContents; } diff --git a/packages/cursorless-jetbrains/src/ide/createTextEditor.ts b/packages/cursorless-jetbrains/src/ide/createTextEditor.ts index e8e0982f88..ea6292d763 100644 --- a/packages/cursorless-jetbrains/src/ide/createTextEditor.ts +++ b/packages/cursorless-jetbrains/src/ide/createTextEditor.ts @@ -16,7 +16,7 @@ export function createTextEditor( ide: JetbrainsIDE, editorState: EditorState, ): JetbrainsEditor { - console.log("createTextEditor"); + // console.log("createTextEditor"); const id = editorState.id; const uri = URI.parse(`talon-jetbrains://${id}`); @@ -43,7 +43,7 @@ export function createSelection( document: TextDocument, selection: JbSelection, ): Selection { - console.log("createSelection " + JSON.stringify(selection)); + // console.log("createSelection " + JSON.stringify(selection)); return new Selection( createPosition(selection.anchor), createPosition(selection.active), diff --git a/packages/cursorless-jetbrains/src/ide/setSelections.ts b/packages/cursorless-jetbrains/src/ide/setSelections.ts index 39f7413d9d..80905572c6 100644 --- a/packages/cursorless-jetbrains/src/ide/setSelections.ts +++ b/packages/cursorless-jetbrains/src/ide/setSelections.ts @@ -7,9 +7,9 @@ export function setSelections( editorId: string, selections: Selection[], ): Promise { - console.log("setSelections: " + selections); + // console.log("setSelections: " + selections); const selectionsJson = JSON.stringify(selections); - console.log("setSelections JSON: " + selectionsJson); + // console.log("setSelections JSON: " + selectionsJson); client.setSelection(editorId, selectionsJson); return Promise.resolve(); } diff --git a/packages/cursorless-jetbrains/src/settimeout-polyfill.ts b/packages/cursorless-jetbrains/src/settimeout-polyfill.ts new file mode 100644 index 0000000000..08f59e28bc --- /dev/null +++ b/packages/cursorless-jetbrains/src/settimeout-polyfill.ts @@ -0,0 +1,5 @@ +const global = globalThis as any; + +global.setTimeout = (callback: () => void, _delay: number) => { + callback(); +}; From 3b24f521aa64b33c0575bb939d3a78761f865ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8e?= Date: Sun, 26 Jan 2025 14:58:18 +0100 Subject: [PATCH 34/34] change default runmode to production --- packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts index 1934f876e7..2bcf2936a2 100644 --- a/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts +++ b/packages/cursorless-jetbrains/src/ide/JetbrainsIDE.ts @@ -38,7 +38,7 @@ export class JetbrainsIDE implements IDE { readonly messages: JetbrainsMessages; readonly clipboard: JetbrainsClipboard; readonly capabilities: JetbrainsCapabilities; - readonly runMode: RunMode = "development"; + readonly runMode: RunMode = "production"; readonly visibleNotebookEditors: NotebookEditor[] = []; // private editorMap; // private documentMap;