Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add option to auto switch im when entering insert or replace mode #19

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 82 additions & 44 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,66 +7,84 @@ interface VimIMSwitchSettings {
fcitxRemotePath_macOS: string;
fcitxRemotePath_windows: string;
fcitxRemotePath_linux: string;
IMSwitch_when_insert_mode: boolean;
IMSwitch_developer_logging: boolean;
}

const DEFAULT_SETTINGS: VimIMSwitchSettings = {
fcitxRemotePath_macOS: '/usr/local/bin/fcitx-remote',
fcitxRemotePath_windows: 'C:\\Program Files\\bin\\fcitx-remote',
fcitxRemotePath_linux: '/usr/bin/fcitx-remote',
IMSwitch_when_insert_mode: true,
IMSwitch_developer_logging: false,
}

const pexec = promisify(exec);

enum IMStatus {
None,
Activate,
Deactivate,
Unknown = "Unknown",
Active = "Active",
Inactive = "Inactive",
}

export default class VimIMSwitchPlugin extends Plugin {
settings: VimIMSwitchSettings;
imStatus = IMStatus.None;
imStatus = IMStatus.Unknown;
fcitxRemotePath = "";

private editorMode: 'cm5' | 'cm6' = null;
private initialized = false;
private cmEditor: CodeMirror.Editor = null;

debug_log(content: any) {
if (this.settings?.IMSwitch_developer_logging) {
console.log(content);
}
}

async onload() {
console.log('loading plugin VimIMSwitchPlugin.');
console.log('Vim Input Method Switch: loading plugin');

await this.loadSettings();

// this.addStatusBarItem().setText('Vim IM Swith Enabled');
// this.addStatusBarItem().setText('Vim IM Switch Enabled');

this.addSettingTab(new IMSwitchSettingTab(this.app, this));

this.app.workspace.on('quit', async () => {
await this.deactivateIM();
});

this.app.workspace.on('file-open', async (file: TFile) => {
console.log("file-open")
if (!this.initialized)
this.debug_log("Vim Input Method Switch: file-open")
if (!this.initialized && file)
await this.initialize();
// {mode: string, ?subMode: string} object. Modes: "insert", "normal", "replace", "visual". Visual sub-modes: "linewise", "blockwise"}
if (this.cmEditor) {
// default is normal mode, try to deactivate the IM.
await this.deactivateIM();
if (this.imStatus == IMStatus.Unknown) {
await this.getFcitxRemoteStatus();
}
this.cmEditor.off("vim-mode-change", this.onVimModeChange);
this.cmEditor.on("vim-mode-change", this.onVimModeChange);
}
});

// Used when we open a new markdown view by "split vertically",
// Used when we open a new markdown view by "split vertically",
// which will not trigger 'file-open' event on obsidian v0.15.6
this.app.workspace.on('active-leaf-change', async (leaf: WorkspaceLeaf) => {
console.log("active-leaf-change")
if(this.app.workspace.activeLeaf.view.getViewType() == "markdown")
{
console.log("focus on markdown view")
this.debug_log("Vim Input Method Switch: active-leaf-change")
if(this.app.workspace.activeLeaf.view.getViewType() == "markdown") {
this.debug_log("Vim Input Method Switch: focus on markdown view")
if (!this.initialized)
await this.initialize();
// {mode: string, ?subMode: string} object. Modes: "insert", "normal", "replace", "visual". Visual sub-modes: "linewise", "blockwise"}
if (this.cmEditor) {
// default is normal mode, try to deactivate the IM.
await this.deactivateIM();
if (this.imStatus == IMStatus.Unknown) {
await this.getFcitxRemoteStatus();
}
this.cmEditor.off("vim-mode-change", this.onVimModeChange);
this.cmEditor.on("vim-mode-change", this.onVimModeChange);
}
Expand All @@ -75,88 +93,92 @@ export default class VimIMSwitchPlugin extends Plugin {
}

async initialize() {
if (this.initialized)
if (this.initialized) {
return;
console.log("initialize")
// Determine if we have the legacy Obsidian editor (CM5) or the new one (CM6).
// This is only available after Obsidian is fully loaded, so we do it as part of the `file-open` event.
if ('editor:toggle-source' in (this.app as any).commands.editorCommands) {
this.editorMode = 'cm6';
console.log('VimIMSwitchPlugin: using CodeMirror 6 mode');
} else {
this.editorMode = 'cm5';
console.log('VimIMSwitchPlugin: using CodeMirror 5 mode');
}

this.debug_log("Vim Input Method Switch: initializing")

// For CM6 this actually returns an instance of the object named CodeMirror from cm_adapter of codemirror_vim
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
if (this.editorMode == 'cm6')
this.cmEditor = (view as any).sourceMode?.cmEditor?.cm?.cm;
else
this.cmEditor = (view as any).sourceMode?.cmEditor;
this.cmEditor = (view as any).sourceMode?.cmEditor?.cm?.cm;

// on Obsidian v0.15.6, we can't reuse cmEditor got at the beginning of application
// we need to get cmEditor again for every 'file-open'
// we need to get cmEditor again for every 'file-open'
// and every 'split vertically' and every 'split horizontally'
// this.initialized = true;
}

onVimModeChange = async (cm: any) => {
// this.debug_log("Vim Input Method Switch: Vim mode change to : " + cm.mode);

if (cm.mode == "normal" || cm.mode == "visual") {
await this.getFcitxRemoteStatus();
if (this.imStatus == IMStatus.Activate) {
if (this.imStatus == IMStatus.Active) {
await this.deactivateIM();
}
} else if (cm.mode == "insert" || cm.mode == "replace") {
if (this.imStatus == IMStatus.Activate) {
if (this.imStatus == IMStatus.Inactive && this.settings.IMSwitch_when_insert_mode == true) {
await this.activateIM();
}
}
}

async runCmd(cmd: string, args: string[] = []) : Promise<string>{
async runCmd(cmd: string, args: string[] = []) : Promise<string> {
const output = await pexec(`${cmd} ${args.join(" ")}`);
return output.stdout;
return output.stdout || output.stderr;
}

async getFcitxRemoteStatus() {
if (this.fcitxRemotePath == "") {
console.log("VIM-IM-Switch-pugin: cannot get fcitx-remote path, please set it correctly.");
this.debug_log("Vim Input Method Switch: cannot get fcitx-remote path, please set it correctly.");
return;
}
let fcitxRemoteOutput = await this.runCmd(this.fcitxRemotePath);
fcitxRemoteOutput = fcitxRemoteOutput.trimRight();
if (fcitxRemoteOutput == "1") {
this.imStatus = IMStatus.Deactivate;
this.imStatus = IMStatus.Inactive;
} else if (fcitxRemoteOutput == "2") {
this.imStatus = IMStatus.Activate;
this.imStatus = IMStatus.Active;
} else {
this.imStatus = IMStatus.None;
this.imStatus = IMStatus.Unknown;
}
console.log("Vim-IM-Swith-plugin: IM status " + this.imStatus.toString());
this.debug_log("Vim Input Method Switch: input method status: " + this.imStatus.toString());
}

async activateIM() {
if (this.fcitxRemotePath == "") {
console.log("VIM-IM-Switch-pugin: cannot get fcitx-remote path, please set it correctly.");
this.debug_log("Vim Input Method Switch: cannot get fcitx-remote path, please set it correctly.");
return;
}
const output = await this.runCmd(this.fcitxRemotePath, ["-o"]);
console.log("Vim-IM-Swith-plugin: activate IM " + output);
this.debug_log("Vim Input Method Switch: activate input method: " + output);

if (/Changing to/gi.test(output) && process.platform == "darwin") { // https://github.com/xcodebuild/fcitx-remote-for-osx/blob/master/fcitx-remote/main.m#L95
this.imStatus = IMStatus.Inactive;
this.debug_log("Vim Input Method Switch: input method status: " + this.imStatus.toString());
}
}

async deactivateIM() {
if (this.fcitxRemotePath == "") {
console.log("VIM-IM-Switch-pugin: cannot get fcitx-remote path, please set it correctly.");
this.debug_log("Vim Input Method Switch: cannot get fcitx-remote path, please set it correctly.");
return;
}
const output = await this.runCmd(this.fcitxRemotePath, ["-c"]);
console.log("Vim-IM-Swith-plugin: deactivate IM " + output);
this.debug_log("Vim Input Method Switch: deactivate input method: " + output);

if (/Changing to/gi.test(output) && process.platform == "darwin") { // https://github.com/xcodebuild/fcitx-remote-for-osx/blob/master/fcitx-remote/main.m#L95
this.imStatus = IMStatus.Inactive;
this.debug_log("Vim Input Method Switch: input method status: " + this.imStatus.toString());
}
}

onunload() {
if (this.cmEditor) {
this.cmEditor.off("vim-mode-change", this.onVimModeChange);
}
console.log('unloading plugin VimIMSwitchPlugin.');
this.debug_log('Vim Input Method Switch: unloading plugin');
}

async loadSettings() {
Expand All @@ -176,7 +198,7 @@ export default class VimIMSwitchPlugin extends Plugin {
this.fcitxRemotePath = this.settings.fcitxRemotePath_windows;
break;
default:
console.log('VIM-IM-Switch-plugin: does not support ' + process.platform + ' currently.');
console.log('Vim Input Method Switch: does not support ' + process.platform + ' currently.');
break;
}
}
Expand Down Expand Up @@ -234,5 +256,21 @@ class IMSwitchSettingTab extends PluginSettingTab {
this.plugin.updateCurrentPath();
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Auto switch input method when entering insert or replace mode')
.addToggle(toggle => toggle.setValue(this.plugin.settings.IMSwitch_when_insert_mode)
.onChange((value) => {
this.plugin.settings.IMSwitch_when_insert_mode = value;
this.plugin.updateCurrentPath();
this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Toggle developer logging')
.addToggle(toggle => toggle.setValue(this.plugin.settings.IMSwitch_developer_logging)
.onChange((value) => {
this.plugin.settings.IMSwitch_developer_logging = value;
this.plugin.updateCurrentPath();
this.plugin.saveSettings();
}));
}
}
20 changes: 10 additions & 10 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"id": "obsidian-vim-im-switch-plugin",
"name": "Vim Input Method Switch",
"version": "1.0.8",
"minAppVersion": "0.9.12",
"description": "Switch input method with fcitx-remote when Vim keymap is enabled.",
"author": "Yuan Chen",
"authorUrl": "https://github.com/yuanotes",
"isDesktopOnly": true
}
{
"id": "obsidian-vim-im-switch-plugin",
"name": "Vim Input Method Switch",
"version": "1.2.1",
"minAppVersion": "1.5.3",
"description": "Switch input method with fcitx-remote when Vim keymap is enabled.",
"author": "Yuan Chen",
"authorUrl": "https://github.com/yuanotes",
"isDesktopOnly": true
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "obsidian-vim-im-switch-plugin",
"version": "0.12.0",
"version": "1.2.1",
"description": "This is a plugin for Obsidian (https://obsidian.md). Switch input method with fcitx-remote when Vim keymap is enabled.",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w",
"build": "rollup --config rollup.config.js --environment BUILD:production"
"build": "rollup --config rollup.config.js --environment BUILD:production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "",
Expand Down
14 changes: 14 additions & 0 deletions version-bump.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { readFileSync, writeFileSync } from "fs";

const targetVersion = process.env.npm_package_version;

// read minAppVersion from manifest.json and bump version to target version
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));

// update versions.json with target version and minAppVersion from manifest.json
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
versions[targetVersion] = minAppVersion;
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));
14 changes: 9 additions & 5 deletions versions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"1.0.2": "0.9.12",
"1.0.1": "0.9.12",
"1.0.0": "0.9.7"
}
{
"1.0.2": "0.9.12",
"1.0.1": "0.9.12",
"1.0.0": "0.9.7",
"1.0.3": "0.9.12",
"1.1.0": "0.9.12",
"1.2.0": "1.5.3",
"1.2.1": "1.5.3"
}