Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 138 additions & 55 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
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
Expand Down Expand Up @@ -35,34 +64,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.
//
Expand All @@ -73,15 +78,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) {
Expand All @@ -90,32 +95,21 @@ 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: activeEditorState["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,
Expand All @@ -128,10 +122,93 @@ 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")
);

// 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;
}
});

let activeEditor;

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,
}
);




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(
new vscode.RelativePattern(
require("os").homedir() + "/.cursorless/",
"**/*"
"*-state.json"
)
);

Expand All @@ -150,8 +227,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) => {
Expand Down