From 22cfac52a5a3d9a3b565027399a7bffa4df2b111 Mon Sep 17 00:00:00 2001 From: dyamito942 Date: Tue, 20 Sep 2022 08:55:42 -0400 Subject: [PATCH 1/4] Separate out editor state application logic --- src/extension.ts | 97 ++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 8c8060b..070ab11 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -35,34 +35,10 @@ export async function activate(context: vscode.ExtensionContext) { * Reads the state of the primary ("superior") editor and makes VS Code mimic it * (current file, selections, scroll area, etc.) */ - async function applyPrimaryEditorState() { - const fs = require("fs"); - const os = require("os"); - const path = require("path"); - - const SIDECAR_FEATURE_FLAG_PATH = path.join( - os.homedir(), - ".cursorless/sidecar-enabled" - ); - - // Allowed disabling the sidecar with a flag, so you can actually use other parts of VS Code - // when needed. - if (!readFlagFile(SIDECAR_FEATURE_FLAG_PATH, true)) { - console.log( - `applyPrimaryEditorState: ${SIDECAR_FEATURE_FLAG_PATH} set to false; not synchronizing` - ); - return; - } - - // TODO(pcohen): make this generic across editors - // TODO(pcohen): diff the state against the previous state - let state = JSON.parse( - fs.readFileSync(os.homedir() + "/.cursorless/editor-state.json") - ); - let activeEditorState = state["activeEditor"]; - - let editor = vscode.window.activeTextEditor; - + async function applyEditorStateToVscodeEditor( + editorState: any, + editor: vscode.TextEditor + ) { // If we got into a state where the editor has local changes, always revert them. Otherwise all subsequent // commands will fail. // @@ -73,15 +49,15 @@ export async function activate(context: vscode.ExtensionContext) { await commands.executeCommand("workbench.action.files.revert"); } - let destPath = activeEditorState["path"]; + let destPath = editorState["path"]; // TODO(pcohen): forward the language mode from the source editor, rather than just relying on the file extension // (see workbench.action.editor.changeLanguageMode, but also, there is a direct // API for this: vscode.languages.setLanguageId, and a voice command: "change language Python") // Prefer the temporary file if it's available - if (activeEditorState["temporaryFilePath"]) { - destPath = activeEditorState["temporaryFilePath"]; + if (editorState["temporaryFilePath"]) { + destPath = editorState["temporaryFilePath"]; } if (destPath !== editor?.document.uri.path) { @@ -97,25 +73,23 @@ export async function activate(context: vscode.ExtensionContext) { } commands.executeCommand("revealLine", { - lineNumber: activeEditorState["firstVisibleLine"] - 1, + lineNumber: editorState["firstVisibleLine"] - 1, at: "top", }); if (editor) { - if (activeEditorState["selections"]) { - editor.selections = activeEditorState["selections"].map( - (selection: any) => { - return new vscode.Selection( - selection.anchor.line, - selection.anchor.column, - selection.active.line, - selection.active.column - ); - } - ); + if (editorState["selections"]) { + editor.selections = editorState["selections"].map((selection: any) => { + return new vscode.Selection( + selection.anchor.line, + selection.anchor.column, + selection.active.line, + selection.active.column + ); + }); } else { - // TODO(rntz): migrate to |activeEditorState["selections"]| - editor.selections = activeEditorState["cursors"].map( + // TODO(rntz): migrate to |editorState["selections"]| + editor.selections = editorState["cursors"].map( (cursor: any) => new vscode.Selection( cursor.line, @@ -128,6 +102,39 @@ export async function activate(context: vscode.ExtensionContext) { } } + async function applyPrimaryEditorState() { + const fs = require("fs"); + const os = require("os"); + const path = require("path"); + + const SIDECAR_FEATURE_FLAG_PATH = path.join( + os.homedir(), + ".cursorless/sidecar-enabled" + ); + + // Allowed disabling the sidecar with a flag, so you can actually use other parts of VS Code + // when needed. + if (!readFlagFile(SIDECAR_FEATURE_FLAG_PATH, true)) { + console.log( + `applyPrimaryEditorState: ${SIDECAR_FEATURE_FLAG_PATH} set to false; not synchronizing` + ); + return; + } + + // TODO(pcohen): make this generic across editors + // TODO(pcohen): diff the state against the previous state + let state = JSON.parse( + fs.readFileSync(os.homedir() + "/.cursorless/editor-state.json") + ); + + if (vscode.window.activeTextEditor) { + await applyEditorStateToVscodeEditor( + state["editors"][0], + vscode.window.activeTextEditor + ); + } + } + const watcher = vscode.workspace.createFileSystemWatcher( new vscode.RelativePattern( require("os").homedir() + "/.cursorless/", From 3f00a48a56c6101373d06fd5b8233d447231b7ab Mon Sep 17 00:00:00 2001 From: dyamito942 Date: Tue, 20 Sep 2022 08:56:00 -0400 Subject: [PATCH 2/4] Make watcher only watch the editor-state file --- src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 070ab11..633bc08 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -138,7 +138,7 @@ export async function activate(context: vscode.ExtensionContext) { const watcher = vscode.workspace.createFileSystemWatcher( new vscode.RelativePattern( require("os").homedir() + "/.cursorless/", - "**/*" + "*editor-state.json" ) ); From d04badf111b714463e0ca853269f3bf1fe260c91 Mon Sep 17 00:00:00 2001 From: dyamito942 Date: Tue, 20 Sep 2022 09:59:37 -0400 Subject: [PATCH 3/4] support multiple editors --- src/extension.ts | 66 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 633bc08..6403237 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,4 +1,5 @@ import * as vscode from "vscode"; +import { property, map, indexOf, zipObject,keys } from "lodash"; import { commands, Uri } from "vscode"; export async function activate(context: vscode.ExtensionContext) { @@ -66,17 +67,8 @@ export async function activate(context: vscode.ExtensionContext) { // TODO(pcohen): we need to make this blocking; I believe the commands below // run too early when the currently opened file is changed. await commands.executeCommand("vscode.open", Uri.file(destPath)); - - // Close the other tabs that might have been opened. - // TODO(pcohen): this seems to always leave one additional tab open. - await commands.executeCommand("workbench.action.closeOtherEditors"); } - commands.executeCommand("revealLine", { - lineNumber: editorState["firstVisibleLine"] - 1, - at: "top", - }); - if (editor) { if (editorState["selections"]) { editor.selections = editorState["selections"].map((selection: any) => { @@ -127,22 +119,58 @@ export async function activate(context: vscode.ExtensionContext) { fs.readFileSync(os.homedir() + "/.cursorless/editor-state.json") ); - if (vscode.window.activeTextEditor) { - await applyEditorStateToVscodeEditor( - state["editors"][0], - vscode.window.activeTextEditor - ); + // map tempfilepath to vscode editor (eventually id to vscode editor) + const editorMap = zipObject( + map(vscode.window.visibleTextEditors, property("document.fileName")), + vscode.window.visibleTextEditors + ); + + let differentVisibleWindows = false; + let superiorEditorVisibleFileNames = map( + state["editors"], + property("temporaryFilePath") + ); + + superiorEditorVisibleFileNames.forEach((superiorEditorFileName) => { + if (keys(editorMap).indexOf(superiorEditorFileName) === -1) { + differentVisibleWindows = true; + } + }); + + + if (differentVisibleWindows) { + // Close the other tabs that might have been opened. + // TODO(pcohen): this seems to always leave one additional tab open. + await commands.executeCommand("workbench.action.closeAllEditors"); + + state["editors"].forEach(async (editorState: any) => { + let vscodeEditor = await vscode.window.showTextDocument( + Uri.file(editorState["temporaryFilePath"]), + { + viewColumn: vscode.ViewColumn.Beside, + } + ); + await applyEditorStateToVscodeEditor(editorState, vscodeEditor); + }); + } else { + state["editors"].forEach(async (editorState: any) => { + await applyEditorStateToVscodeEditor( + editorState, + editorMap[editorState["temporaryFilePath"]] + ); + }); } } const watcher = vscode.workspace.createFileSystemWatcher( new vscode.RelativePattern( require("os").homedir() + "/.cursorless/", - "*editor-state.json" + "*-state.json" ) ); watcher.onDidChange((uri) => { + console.log('changed'); applyPrimaryEditorState(); }); @@ -157,8 +185,14 @@ export async function activate(context: vscode.ExtensionContext) { // ================================================================================ function vsCodeState(includeEditorContents: boolean = false) { - const editor = vscode.window.activeTextEditor; + return { + editors: map(vscode.window.visibleTextEditors, (textEditor) => { + return vsCodeEditorState(textEditor, includeEditorContents); + }) + } + } + function vsCodeEditorState(editor: vscode.TextEditor, includeEditorContents: boolean = false) { let result = { path: editor?.document.uri.path, cursors: editor?.selections.map((s) => { From b8870e9f70eae5dafe36d15a8cea086d9fbdf49c Mon Sep 17 00:00:00 2001 From: dyamito942 Date: Wed, 21 Sep 2022 22:50:14 -0400 Subject: [PATCH 4/4] focus editor --- src/extension.ts | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 6403237..e932d7b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,6 +2,34 @@ import * as vscode from "vscode"; import { property, map, indexOf, zipObject,keys } from "lodash"; import { commands, Uri } from "vscode"; + +const columnFocusCommands = { + [vscode.ViewColumn.One]: "workbench.action.focusFirstEditorGroup", + [vscode.ViewColumn.Two]: "workbench.action.focusSecondEditorGroup", + [vscode.ViewColumn.Three]: "workbench.action.focusThirdEditorGroup", + [vscode.ViewColumn.Four]: "workbench.action.focusFourthEditorGroup", + [vscode.ViewColumn.Five]: "workbench.action.focusFifthEditorGroup", + [vscode.ViewColumn.Six]: "workbench.action.focusSixthEditorGroup", + [vscode.ViewColumn.Seven]: "workbench.action.focusSeventhEditorGroup", + [vscode.ViewColumn.Eight]: "workbench.action.focusEighthEditorGroup", + [vscode.ViewColumn.Nine]: "workbench.action.focusNinthEditorGroup", + [vscode.ViewColumn.Active]: "", + [vscode.ViewColumn.Beside]: "", +}; + +export async function focusEditor(editor: vscode.TextEditor) { + const viewColumn = getViewColumn(editor); + if (viewColumn != null) { + await commands.executeCommand(columnFocusCommands[viewColumn]); + } +} + +function getViewColumn(editor: vscode.TextEditor): vscode.ViewColumn | undefined { + if (editor.viewColumn != null) { + return editor.viewColumn; + } +} + export async function activate(context: vscode.ExtensionContext) { // ================================================================================ // Applying the the primary/other editor's state @@ -137,6 +165,7 @@ export async function activate(context: vscode.ExtensionContext) { } }); + let activeEditor; if (differentVisibleWindows) { // Close the other tabs that might have been opened. @@ -150,16 +179,30 @@ export async function activate(context: vscode.ExtensionContext) { viewColumn: vscode.ViewColumn.Beside, } ); + + + + + if (editorState["active"] === true) { + activeEditor = vscodeEditor; + } + await applyEditorStateToVscodeEditor(editorState, vscodeEditor); }); } else { state["editors"].forEach(async (editorState: any) => { + if (editorState["active"] === true) { + activeEditor = editorMap[editorState["temporaryFilePath"]]; + } await applyEditorStateToVscodeEditor( editorState, editorMap[editorState["temporaryFilePath"]] ); }); } + + await focusEditor(activeEditor); + } const watcher = vscode.workspace.createFileSystemWatcher( @@ -170,7 +213,6 @@ export async function activate(context: vscode.ExtensionContext) { ); watcher.onDidChange((uri) => { - console.log('changed'); applyPrimaryEditorState(); });