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

[Feature request] Lightspeed.nvim-similar execution for "Jump to Anywhere" #35

Closed
kohane27 opened this issue Mar 31, 2022 · 5 comments · Fixed by #39
Closed

[Feature request] Lightspeed.nvim-similar execution for "Jump to Anywhere" #35

kohane27 opened this issue Mar 31, 2022 · 5 comments · Fixed by #39

Comments

@kohane27
Copy link

Hello mrjackphil! Thank you for creating such an awesome plugin! It provides an even more fluent Vim experience.

Introduction

In short, Lightspeed.nvim is a motion plugin very similar to vim-easymotion. The reason for a Lightspeed-similar execution is to remove much visual clutter.

With the current "Jump to Anywhere" implementation, if I want to, say, jump to consectetur, this is the screen after triggering activate-jump-to-anywhere:

screenshot-2022-03-31-19-53-56

This is the screen for Lightspeed-simliar implementation:
screenshot-2022-03-31-19-54-23

Current implementation:

Trigger activate-jump-to-anywhere -> all words are parsed for tags -> press the two keys on tag to jump to desired word

Lightspeed-similar implementation:

Trigger activate-jump-to-anywhere -> press first two characters of desired word -> provides tags that matches jumpToAnywhereRegex for jump

Having trouble implementing

I've looked into handleJumpToRegex in main.ts. The crust of the matter is that, how do I capture two extra keypress sequence after triggering activate-jump-to-anywhere?

    handleJumpToRegex = () => {
        const { app, settings: { letters } } = this
        const currentView = app.workspace.activeLeaf.view;
        const mode = this.getMode(currentView);

        ****************************************
        let capturedTwoKeys = "" // e.g., "ow"
        let twoKeysRegex = "\b" + capturedTwoKeys; // replaced `jumpToAnywhereRegex` since it needs to be generated on-the-fly when we trigger `activate-jump-to-anywhere`.
        ****************************************
        
        switch (mode) {
            case VIEW_MODE.LIVE_PREVIEW:
                const cm6Editor: EditorView = (<{ editor?: { cm: EditorView } }>currentView).editor.cm;
                const livePreviewLinks = new LivePreviewRegexProcessor(cm6Editor, letters, **twoKeysRegex**).init();
                this.markPlugin.setLinks(livePreviewLinks);
                this.app.workspace.updateOptions();
                this.handleActions(livePreviewLinks, cm6Editor);
                break;
            case VIEW_MODE.PREVIEW:
                break;
            /* OMITTED */
            default:
                break;
        }
    }

I tried adding an addEventListener: document.addEventListener('keydown', (event) => console.log(event.key));, but it's not captured in Normal mode, only in Insert mode.

I'm also confused as to where I should look for reference: obsidian.d.ts or codemirror?

Any help is much appreciated. Thank you!

@mrjackphil
Copy link
Owner

mrjackphil commented Mar 31, 2022

@kohane27 Hi.

I assume there should be an addition command:

this.addCommand({
    id: "activate-ligthspeed-jump",
    name: "Lightspeed Jump",
    callback: this.action.bind(this, 'lightspeed'),
    hotkeys: [],
});

Add action handler:

action(type: 'link' | 'regexp' | 'lightspeed') {
    if (this.isLinkHintActive) {
        return;
    }

    switch (type) {
        case "link":
            this.handleJumpToLink();
            return
        case "regexp":
            this.handleJumpToRegex();
            return
       case "lightspeed":
           this.handleLightspeedJump();
           return
    }
}

Create function for capturing two more hotkeys (logic not provided in this snippet):

handleLightspeedJump() {
  const keyArray = []
  # Should handle Escape to reject the mode, append to keyArray and stop when length of array is eq 2
  const grabKey = (key) => {  }
  # Event listener should be removed when proceed
  document.addEventListener('keydown', grabKey, { capture: true });
  this.handleJumpToRegex(keyArray.join(''));
}

Use pressed keys as a string for search:

handleJumpToRegex = (stringToSearch?: string) => {
        const {app, settings: {letters, jumpToAnywhereRegex}} = this
        const currentView = app.workspace.activeLeaf.view;
        const mode = this.getMode(currentView);
        const whatToLookAt = stringToSearch || jumpToAnywhereRegex

        switch (mode) {
            case VIEW_MODE.LIVE_PREVIEW:
                const cm6Editor: EditorView = (<{ editor?: { cm: EditorView } }>currentView).editor.cm;
                const livePreviewLinks = new LivePreviewRegexProcessor(cm6Editor, letters, whatToLookAt).init();
                this.markPlugin.setLinks(livePreviewLinks);
                this.app.workspace.updateOptions();
                this.handleActions(livePreviewLinks, cm6Editor);
                break;
            case VIEW_MODE.PREVIEW:
                break;
            case VIEW_MODE.SOURCE:
                const cmEditor: Editor = (currentView as any).sourceMode.cmEditor;
                const links = new RegexpProcessor(cmEditor, whatToLookAt, letters).init();
                this.handleActions(links, cmEditor);
                break;
            default:
                break;
        }
    }

And... it might do the trick.

@mrjackphil
Copy link
Owner

mrjackphil commented Mar 31, 2022

Thank you for showing Lightspeed 😊.

@mrjackphil
Copy link
Owner

@kohane27 Also, try add { capture: true } to the addEventListener

document.addEventListener('keydown', grabKey, { capture: true });

@kohane27
Copy link
Author

kohane27 commented Apr 1, 2022

Hello @mrjackphil . Thank you for responding quickly. I really appreciate it.

Here's a video demo:

simplescreenrecorder-2022-04-01_19.43.37.mp4

To explain the keypresses on screen: my trigger for activate-ligthspeed-jump is s in Normal mode. I first jumped to "voluptate", then "laboris". So any 4 keypresses will take me to anywhere I want!

Thank you for giving pointers and directions. I sort of figured it out with your help:

    handleLightspeedJump() {
        const keyArray = []
        const grabKey = (event) => {
            event.preventDefault();
            // handle Escape to reject the mode
            if (event.key === 'Escape') {
                document.removeEventListener("keydown", grabKey, { capture: true })
            }

            // test if keypress is capitalized
            if (/^[a-z]$/i.test(event.key)) {
                const isCapital = event.shiftKey;
                if (isCapital) {
                    // capture uppercase
                    keyArray.push((event.key).toUpperCase());
                } else {
                    // capture lowercase
                    keyArray.push(event.key);
                }
            }

            // stop when length of array is equal to 2
            if (keyArray.length === 2) {
                this.handleJumpToRegex("\\b" + keyArray.join(""));
                // Event listener should be removed when proceed
                document.removeEventListener("keydown", grabKey, { capture: true })
            }
        }
        document.addEventListener('keydown', grabKey, { capture: true });
    }

This probably isn't the best way to handle it but it works.

I'll try to dim all the text after trigger activate-jump-to-anywhere for some visual indicator.

I'm very happy with the result. Thank you again!

@kohane27
Copy link
Author

kohane27 commented Apr 1, 2022

I've changed all font color to be gray to be more faithful to how lightspeed operates:

simplescreenrecorder-2022-04-01_21.20.52.mp4
    handleLightspeedJump() {
        // get all text color
        const contentContainerColor = document.getElementsByClassName("cm-contentContainer");
        const originalColor = contentContainerColor[0].style.color;
        // change all text color to gray
        contentContainerColor[0].style.color = 'gray';

        const keyArray = []
        const grabKey = (event) => {
            event.preventDefault();
            // handle Escape to reject the mode
            if (event.key === 'Escape') {
                document.removeEventListener("keydown", grabKey, { capture: true })
            }

            // test if keypress is capitalized
            if (/^[a-z]$/i.test(event.key)) {
                const isCapital = event.shiftKey;
                if (isCapital) {
                    // capture uppercase
                    keyArray.push((event.key).toUpperCase());
                } else {
                    // capture lowercase
                    keyArray.push(event.key);
                }
            }

            // stop when length of array is equal to 2
            if (keyArray.length === 2) {
                this.handleJumpToRegex("\\b" + keyArray.join(""));
                // removing eventListener after proceeded
                document.removeEventListener("keydown", grabKey, { capture: true })
                contentContainerColor[0].style.color = originalColor;
            }
        }
        document.addEventListener('keydown', grabKey, { capture: true });
    }

I want the font color to return to its original color after selecting the tag (currently it's before selecting the tag), but not sure how to do so. However I'm happy with the end result. Thanks again for creating such an awesome plugin!

@kohane27 kohane27 closed this as completed Apr 3, 2022
kometenstaub added a commit to kometenstaub/obsidian-jump-to-link that referenced this issue Jun 15, 2022
Also fixed a bug concerning source and legacy editor mode names (Obsidian v0.15.0).

Original code from @mrjackphil and @kohane27, cf.
mrjackphil#35
kometenstaub added a commit to kometenstaub/obsidian-jump-to-link that referenced this issue Jun 15, 2022
Also fixed a bug concerning source and legacy editor mode names (Obsidian v0.15.0).

Original code from @mrjackphil and @kohane27, cf.
mrjackphil#35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants