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

Support for Look Up "<word>" popup menu #1191

Closed
ghost opened this issue Apr 21, 2021 · 8 comments · Fixed by #1312
Closed

Support for Look Up "<word>" popup menu #1191

ghost opened this issue Apr 21, 2021 · 8 comments · Fixed by #1312
Milestone

Comments

@ghost
Copy link

ghost commented Apr 21, 2021

A clear and concise description of what is hard to do
In Apple's builtin applications and in the non-MacVim third party applications I use, force-clicking on a word shows a popup menu with information about the word (a dictionary definition and so on). One can also view this popup menu by right-clicking on the word and selecting Look Up "word". See the image below. I use this when writing to quickly look up words in the dictionary without having to open another window or application.

Describe the solution you'd like
Implement a way to bind the opening of this popup window to a key (or keys) or mouse action. This way we can customise how to open this menu and we can add this to the MacVim gui popup via the builtin :menu command. It would likely be a good idea to set the default settings to emulate MacOS behaviour.

Describe alternatives you've considered
I have looked into opening this window with apple script, but I could not find a way. One alternative is to write a function that takes the word under the cursor and executes :!open dict://word. This will show the word in the builtin Dictionary.app. This opens up another application, however, and shows a lot more information than just a simple definition. The Look Up window allows you to open the word in Dictionary.app anyway.

Screenshot 2021-04-21 at 13 57 03

@ychin ychin added this to the Backlog milestone Apr 22, 2021
@ychin
Copy link
Member

ychin commented Apr 22, 2021

Thanks for filing this. It's been on my to-do after we refactored to the new renderer. There are really a couple things we should do on this:

  1. Support native lookup (Force Touch, three-finger tap, or Cmd-Ctrl-D). This requires hooking up to Apple's text input client API so macOS knows what the text under the cursors are, but the API is not terribly well documented so I kind of punted on that work. After we do that you will be able to just use the regular Cmd-Ctrl-D to look up the text under the cursor.
    • Also, currently force touch is hijacked by MacVim to allow for mapping it using <ForceClick>. Probably need to do something there to allow the user to choose what to do.
    • I believe this will also allows us to hook up the macOS built-in spellchecker for Touch Bar.
  2. Support lookup as a Vim function, so you can do :call mvim_lookup("http://www.google.com") or something like that (macOS's lookups support web URLs as well as dictionary). This is easier to do but I want to figure out how to do (1) first.

@eirnym
Copy link
Contributor

eirnym commented Apr 22, 2021

I also prefer to have a switch in vimrc to turn this off

@ghost
Copy link
Author

ghost commented Apr 22, 2021

Thanks for filing this. It's been on my to-do after we refactored to the new renderer. There are really a couple things we should do on this:

  1. Support native lookup (Force Touch, three-finger tap, or Cmd-Ctrl-D). This requires hooking up to Apple's text input client API so macOS knows what the text under the cursors are, but the API is not terribly well documented so I kind of punted on that work. After we do that you will be able to just use the regular Cmd-Ctrl-D to look up the text under the cursor.

    • Also, currently force touch is hijacked by MacVim to allow for mapping it using <ForceClick>. Probably need to do something there to allow the user to choose what to do.
    • I believe this will also allows us to hook up the macOS built-in spellchecker for Touch Bar.
  2. Support lookup as a Vim function, so you can do :call mvim_lookup("http://www.google.com") or something like that (macOS's lookups support web URLs as well as dictionary). This is easier to do but I want to figure out how to do (1) first.

Yes agreed a native implementation would be ideal, though if it proves to be too much work then (2) would suffice as well. I am glad to see that it is on the todo list.

@ychin
Copy link
Member

ychin commented Apr 23, 2021

@eirnym can you explain why you would like a switch to turn this off? Lookup requires active user action to activate. It's not a passive feature, and most editors / GUI applications support it and there are system-wide settings that allow you to configure how to activate it in the OS.

@eirnym
Copy link
Contributor

eirnym commented Apr 23, 2021

This is the least used by me macOS feature. Problem there's no good dictionaries for Polish and many other languages and no way to install your own from open source dictionaries

@ychin
Copy link
Member

ychin commented Apr 23, 2021

Right but that’s not what I’m asking about. My point is if you don’t actively use this feature you can just not use it. No reason to include a preference to turn it off. It’s not like the program will randomly pop up the dictionary for you.

Also, lookup supports other things like Wikipedia and URL previews. It’s not just for dictionary.

@eirnym
Copy link
Contributor

eirnym commented Apr 24, 2021

With macOS apps I can turn this feature off in preferences (and I do). Why this feature must be turned on in MacVim while it's turned off in macOS?

@ychin ychin modified the milestones: Backlog, Release 175 Sep 30, 2022
ychin added a commit to ychin/macvim that referenced this issue Oct 11, 2022
This adds support for looking up data under the mouse cursor. Usually it
will bring up a dictionary, but other times it could be a Wikipedia
article, Siri knowledge, etc. Apple doesn't really have a good name for
it, other than "looking up data", "quick look" (a confusingly similar
name with the other Quick Look OS feature), or "show definition". You
can activate this by doing Ctrl-Cmd-D when the mouse is over a cursor.
If you have a trackpad, you can also either activate this using Force
click or three-finger tap (depends on your system preference settings).

Note that for Force click, this could potentially make it impossible to
use the MacVim `<ForceClick>` mapping in Vim, which allows you to map a
force click to a Vim command (macvim-dev#716). This is handled by having a new
setting (under a new "Input" preference pane which will have more
populated later) that allows you to choose whether to use Force click
for data lookup or Vim's `<ForceClick>` mapping. If you have configured
to use three-finger taps though this setting wouldn't do anything, and
`<ForceClick>` is always send to the Vim mapping.

Also, this is lacking a lot of features that a normal macOS application
would get, e.g. looking up selected texts (e.g. if you have "ice cream",
you may want to select the whole thing to look up the phrase, rather
than just "ice" or "cream"), data detector, and much more (e.g. custom
API support). They will be done later as part of macvim-dev#1311.

Fix macvim-dev#1191
Part of Epic macvim-dev#1311, which contains other items to be implemented.

Technical details below:

The way the OS knows how to look up the data and present it is by
talking to the NSTextInput/NSTextInputClient. Previously MacVim
implemented NSTextInput partially, and didn't implement the critical
firstRectForCharacterRange:actualRange and characterIndexForPoint:
functions. First, in this change we change from NSTextInput to
NSTextInputClient (which is the newer non-deprecated version), and
implement those functions, which allows the OS to query the text
storage.

By default, the OS sends a quickLookWithEvent: call to us whenever the
lookup happens but for some odd reason this isn't automatic for Force
clicks, presumably because some apps want to handle Force clicks
manually (this is why some apps only work for three-finger taps but not
Force clicks for lookups). This isn't documented but I found references
in iTerm/Firefox, and basically we just need to manually handle it and
send off quickLookWithEvent: when handling Force clicks.

For implementing the NSTextInputClient properly, the main issue is
making sure that can work properly with input methods / marked texts,
which is the other primary purpose for this class (other than inputting
keys). For data lookups, I'm represending the grid as a row-major text
(with no newline/space in between) and expose that to the OS. This
already has some issue because it doesn't handle Vim vertical splits
well, as MacVim doesn't really have access to detailed Vim text buffers
easily (unless we do a lot of calls back-and-forth). This means wrapped
texts won't be looked up properly, which I think is ok. Also, the OS
APIs deal with UTF-8 indices, so we can't just convert row/column to raw
indices and have to do a lot of character length calculations
(especially for wide chars like CJK or emojis) to make sure the returned
ranges are consistent and valid. For marked texts though, this presents
a challenge because Vim doesn't really have a strong enough API to
communicate back-and-forth about the marked positions and whatnot (it
only let the GUI know where the current cursor is), and it's hard to
implement APIs like `markedRange` properly because some marked texts
could be hidden or wrapped (if you implement some of these functions
improperly Apple's input methods could start misbehaving especially when
you use arrow keys to navigate). In the end I kept the original
implementation for treating the marked texts as a range starting from 0,
*only* when we have marked text. Kind of a hack but this makes sure we
work both in marked text mode (i.e. when inputting texts) and when doing
lookups. For simplicity I made it so that you can't do data lookups when
in marked text mode now.

Data detection:

Note that the default implementation is quite bare, and lacks a lot of
smart data detection. For example, if you put your mouse over a URL, it
won't properly select the whole URL, and addresses and dates for example
also won't get grouped together properly. This is because these require
additional implementation (e.g. using NSDataDetector) instead of coming
"for free", and will be handled later. In fact, Apple's WebKit and
NSTextView cheats by calling an internal API framework called "Reveal"
(which you can find out by intercepting NSTextView's calls and/or
looking at WebKit's source code) which is much more powerful and
supports looking up package tracking, airline info, and more, but it's
not available to third-party software (that's why Safari's lookup is so
much better than Chrome/Firefox's).

This isn't tested right now. Future task needs to add XCTest support to
properly test this as there are a lot of edge cases involved here.
ychin added a commit to ychin/macvim that referenced this issue Oct 11, 2022
This adds support for looking up data under the mouse cursor. Usually it
will bring up a dictionary, but other times it could be a Wikipedia
article, Siri knowledge, etc. Apple doesn't really have a good name for
it, other than "looking up data", "quick look" (a confusingly similar
name with the other Quick Look OS feature), or "show definition". You
can activate this by doing Ctrl-Cmd-D when the mouse is over a cursor.
If you have a trackpad, you can also either activate this using Force
click or three-finger tap (depends on your system preference settings).

Note that for Force click, this could potentially make it impossible to
use the MacVim `<ForceClick>` mapping in Vim, which allows you to map a
force click to a Vim command (macvim-dev#716). This is handled by having a new
setting (under a new "Input" preference pane which will have more
populated later) that allows you to choose whether to use Force click
for data lookup or Vim's `<ForceClick>` mapping. If you have configured
to use three-finger taps though this setting wouldn't do anything, and
`<ForceClick>` is always send to the Vim mapping.

Also, this is lacking a lot of features that a normal macOS application
would get, e.g. looking up selected texts (e.g. if you have "ice cream",
you may want to select the whole thing to look up the phrase, rather
than just "ice" or "cream"), data detector, and much more (e.g. custom
API support). They will be done later as part of macvim-dev#1311.

Technical details below:

The way the OS knows how to look up the data and present it is by
talking to the NSTextInput/NSTextInputClient. Previously MacVim
implemented NSTextInput partially, and didn't implement the critical
firstRectForCharacterRange:actualRange and characterIndexForPoint:
functions. First, in this change we change from NSTextInput to
NSTextInputClient (which is the newer non-deprecated version), and
implement those functions, which allows the OS to query the text
storage.

By default, the OS sends a quickLookWithEvent: call to us whenever the
lookup happens but for some odd reason this isn't automatic for Force
clicks, presumably because some apps want to handle Force clicks
manually (this is why some apps only work for three-finger taps but not
Force clicks for lookups). This isn't documented but I found references
in iTerm/Firefox, and basically we just need to manually handle it and
send off quickLookWithEvent: when handling Force clicks.

For implementing the NSTextInputClient properly, the main issue is
making sure that can work properly with input methods / marked texts,
which is the other primary purpose for this class (other than inputting
keys). For data lookups, I'm represending the grid as a row-major text
(with no newline/space in between) and expose that to the OS. This
already has some issue because it doesn't handle Vim vertical splits
well, as MacVim doesn't really have access to detailed Vim text buffers
easily (unless we do a lot of calls back-and-forth). This means wrapped
texts won't be looked up properly, which I think is ok. Also, the OS
APIs deal with UTF-8 indices, so we can't just convert row/column to raw
indices and have to do a lot of character length calculations
(especially for wide chars like CJK or emojis) to make sure the returned
ranges are consistent and valid. For marked texts though, this presents
a challenge because Vim doesn't really have a strong enough API to
communicate back-and-forth about the marked positions and whatnot (it
only let the GUI know where the current cursor is), and it's hard to
implement APIs like `markedRange` properly because some marked texts
could be hidden or wrapped (if you implement some of these functions
improperly Apple's input methods could start misbehaving especially when
you use arrow keys to navigate). In the end I kept the original
implementation for treating the marked texts as a range starting from 0,
*only* when we have marked text. Kind of a hack but this makes sure we
work both in marked text mode (i.e. when inputting texts) and when doing
lookups. For simplicity I made it so that you can't do data lookups when
in marked text mode now.

Data detection:

Note that the default implementation is quite bare, and lacks a lot of
smart data detection. For example, if you put your mouse over a URL, it
won't properly select the whole URL, and addresses and dates for example
also won't get grouped together properly. This is because these require
additional implementation (e.g. using NSDataDetector) instead of coming
"for free", and will be handled later. In fact, Apple's WebKit and
NSTextView cheats by calling an internal API framework called "Reveal"
(which you can find out by intercepting NSTextView's calls and/or
looking at WebKit's source code) which is much more powerful and
supports looking up package tracking, airline info, and more, but it's
not available to third-party software (that's why Safari's lookup is so
much better than Chrome/Firefox's).

This isn't tested right now. Future task needs to add XCTest support to
properly test this as there are a lot of edge cases involved here.

Fix macvim-dev#1191
Part of Epic macvim-dev#1311, which contains other items to be implemented.
@ychin
Copy link
Member

ychin commented Oct 11, 2022

#1312 will add support for this! This turned out to be a much larger task than I imagined because the degree to which you want to support this really depends on how much time you want to spend on it (e.g. supporting lookups for selected texts, data detection for URLs), and I feel like there are a lot of more ways to extend this such as custom API support. I made an epic #1311 to track the work for this so it's easier to see them in one place as they are related to each other. Annoyingly Apple uses private APIs for their implementation for native text views and WebKit/Safari, which is why third-party apps' implementation can never work as well as the native ones (e.g. Firefox or Chrome). For example, if you have a flight number (e.g. "AS 123") or package number Safari or TextEdit can natively understand how to show them, but other apps won't (unless they specifically implemented something for that). Just one of those builtin limitations.

After the initial PR though you should already be able to use Ctrl-Cmd-D or trackpad to show definition under work.

With macOS apps I can turn this feature off in preferences (and I do). Why this feature must be turned on in MacVim while it's turned off in macOS?

There isn't a way to turn this feature off completely. You can tell the OS to not do this with the trackpad which MacVim will respect of course, but you cannot disable Ctrl-Cmd-D as far as I can tell. I don't really think it will be a big deal in practice. You have to intentionally trigger this for it to show up.

ychin added a commit to ychin/macvim that referenced this issue Oct 11, 2022
This adds support for looking up data under the mouse cursor. Usually it
will bring up a dictionary, but other times it could be a Wikipedia
article, Siri knowledge, etc. Apple doesn't really have a good name for
it, other than "looking up data", "quick look" (a confusingly similar
name with the other Quick Look OS feature), or "show definition". You
can activate this by doing Ctrl-Cmd-D when the mouse is over a cursor.
If you have a trackpad, you can also either activate this using Force
click or three-finger tap (depends on your system preference settings).

Note that for Force click, this could potentially make it impossible to
use the MacVim `<ForceClick>` mapping in Vim, which allows you to map a
force click to a Vim command (macvim-dev#716). This is handled by having a new
setting (under a new "Input" preference pane which will have more
populated later) that allows you to choose whether to use Force click
for data lookup or Vim's `<ForceClick>` mapping. If you have configured
to use three-finger taps though this setting wouldn't do anything, and
`<ForceClick>` is always send to the Vim mapping.

Also, this is lacking a lot of features that a normal macOS application
would get, e.g. looking up selected texts (e.g. if you have "ice cream",
you may want to select the whole thing to look up the phrase, rather
than just "ice" or "cream"), data detector, and much more (e.g. custom
API support). They will be done later as part of macvim-dev#1311.

Technical details below:

The way the OS knows how to look up the data and present it is by
talking to the NSTextInput/NSTextInputClient. Previously MacVim
implemented NSTextInput partially, and didn't implement the critical
firstRectForCharacterRange:actualRange and characterIndexForPoint:
functions. First, in this change we change from NSTextInput to
NSTextInputClient (which is the newer non-deprecated version), and
implement those functions, which allows the OS to query the text
storage.

By default, the OS sends a quickLookWithEvent: call to us whenever the
lookup happens but for some odd reason this isn't automatic for Force
clicks, presumably because some apps want to handle Force clicks
manually (this is why some apps only work for three-finger taps but not
Force clicks for lookups). This isn't documented but I found references
in iTerm/Firefox, and basically we just need to manually handle it and
send off quickLookWithEvent: when handling Force clicks.

For implementing the NSTextInputClient properly, the main issue is
making sure that can work properly with input methods / marked texts,
which is the other primary purpose for this class (other than inputting
keys). For data lookups, I'm representing the grid as a row-major text
(with no newline/space in between) and expose that to the OS. This
already has some issue because it doesn't handle Vim vertical splits
well, as MacVim doesn't really have access to detailed Vim text buffers
easily (unless we do a lot of calls back-and-forth). This means wrapped
texts won't be looked up properly, which I think is ok. Also, the OS
APIs deal with UTF-8 indices, so we can't just convert row/column to raw
indices and have to do a lot of character length calculations
(especially for wide chars like CJK or emojis) to make sure the returned
ranges are consistent and valid. For marked texts though, this presents
a challenge because Vim doesn't really have a strong enough API to
communicate back-and-forth about the marked positions and whatnot (it
only let the GUI know where the current cursor is), and it's hard to
implement APIs like `markedRange` properly because some marked texts
could be hidden or wrapped (if you implement some of these functions
improperly Apple's input methods could start misbehaving especially when
you use arrow keys to navigate). In the end I kept the original
implementation for treating the marked texts as a range starting from 0,
*only* when we have marked text. Kind of a hack but this makes sure we
work both in marked text mode (i.e. when inputting texts) and when doing
lookups. For simplicity I made it so that you can't do data lookups when
in marked text mode now.

Data detection:

Note that the default implementation is quite bare, and lacks a lot of
smart data detection. For example, if you put your mouse over a URL, it
won't properly select the whole URL, and addresses and dates for example
also won't get grouped together properly. This is because these require
additional implementation (e.g. using NSDataDetector) instead of coming
"for free", and will be handled later. In fact, Apple's WebKit and
NSTextView cheats by calling an internal API framework called "Reveal"
(which you can find out by intercepting NSTextView's calls and/or
looking at WebKit's source code) which is much more powerful and
supports looking up package tracking, airline info, and more, but it's
not available to third-party software (that's why Safari's lookup is so
much better than Chrome/Firefox's).

This isn't tested right now. Future task needs to add XCTest support to
properly test this as there are a lot of edge cases involved here.

Fix macvim-dev#1191
Part of Epic macvim-dev#1311, which contains other items to be implemented.
ychin added a commit to ychin/macvim that referenced this issue Oct 11, 2022
This adds support for looking up data under the mouse cursor. Usually it
will bring up a dictionary, but other times it could be a Wikipedia
article, Siri knowledge, etc. Apple doesn't really have a good name for
it, other than "looking up data", "quick look" (a confusingly similar
name with the other Quick Look OS feature), or "show definition". You
can activate this by doing Ctrl-Cmd-D when the mouse is over a cursor.
If you have a trackpad, you can also either activate this using Force
click or three-finger tap (depends on your system preference settings).

Note that for Force click, this could potentially make it impossible to
use the MacVim `<ForceClick>` mapping in Vim, which allows you to map a
force click to a Vim command (macvim-dev#716). This is handled by having a new
setting (under a new "Input" preference pane which will have more
populated later) that allows you to choose whether to use Force click
for data lookup or Vim's `<ForceClick>` mapping. If you have configured
to use three-finger taps though this setting wouldn't do anything, and
`<ForceClick>` is always send to the Vim mapping.

Also, this is lacking a lot of features that a normal macOS application
would get, e.g. looking up selected texts (e.g. if you have "ice cream",
you may want to select the whole thing to look up the phrase, rather
than just "ice" or "cream"), data detector, and much more (e.g. custom
API support). They will be done later as part of macvim-dev#1311.

Technical details below:

The way the OS knows how to look up the data and present it is by
talking to the NSTextInput/NSTextInputClient. Previously MacVim
implemented NSTextInput partially, and didn't implement the critical
firstRectForCharacterRange:actualRange and characterIndexForPoint:
functions. First, in this change we change from NSTextInput to
NSTextInputClient (which is the newer non-deprecated version), and
implement those functions, which allows the OS to query the text
storage.

By default, the OS sends a quickLookWithEvent: call to us whenever the
lookup happens but for some odd reason this isn't automatic for Force
clicks, presumably because some apps want to handle Force clicks
manually (this is why some apps only work for three-finger taps but not
Force clicks for lookups). This isn't documented but I found references
in iTerm/Firefox, and basically we just need to manually handle it and
send off quickLookWithEvent: when handling Force clicks.

For implementing the NSTextInputClient properly, the main issue is
making sure that can work properly with input methods / marked texts,
which is the other primary purpose for this class (other than inputting
keys). For data lookups, I'm representing the grid as a row-major text
(with no newline/space in between) and expose that to the OS. This
already has some issue because it doesn't handle Vim vertical splits
well, as MacVim doesn't really have access to detailed Vim text buffers
easily (unless we do a lot of calls back-and-forth). This means wrapped
texts won't be looked up properly, which I think is ok. Also, the OS
APIs deal with UTF-8 indices, so we can't just convert row/column to raw
indices and have to do a lot of character length calculations
(especially for wide chars like CJK or emojis) to make sure the returned
ranges are consistent and valid. For marked texts though, this presents
a challenge because Vim doesn't really have a strong enough API to
communicate back-and-forth about the marked positions and whatnot (it
only let the GUI know where the current cursor is), and it's hard to
implement APIs like `markedRange` properly because some marked texts
could be hidden or wrapped (if you implement some of these functions
improperly Apple's input methods could start misbehaving especially when
you use arrow keys to navigate). In the end I kept the original
implementation for treating the marked texts as a range starting from 0,
*only* when we have marked text. Kind of a hack but this makes sure we
work both in marked text mode (i.e. when inputting texts) and when doing
lookups. For simplicity I made it so that you can't do data lookups when
in marked text mode now.

Input method:

This change also fixes a quirk in input method as a driveby change.
Previously the logic for calculating the rect for where the candidate
list was quite broken, but now it's calculated correctly using the
desired range and the current cursor position. This matters when say
using Japanese IM and using the left/right arrow to jump to different
sections of the text. If the desired range is in a wrapped line, the new
logic would attempt to pin it to the left-most column of where the
cursor is in the range.

Data detection:

Note that the default implementation is quite bare, and lacks a lot of
smart data detection. For example, if you put your mouse over a URL, it
won't properly select the whole URL, and addresses and dates for example
also won't get grouped together properly. This is because these require
additional implementation (e.g. using NSDataDetector) instead of coming
"for free", and will be handled later. In fact, Apple's WebKit and
NSTextView cheats by calling an internal API framework called "Reveal"
(which you can find out by intercepting NSTextView's calls and/or
looking at WebKit's source code) which is much more powerful and
supports looking up package tracking, airline info, and more, but it's
not available to third-party software (that's why Safari's lookup is so
much better than Chrome/Firefox's).

This isn't tested right now. Future task needs to add XCTest support to
properly test this as there are a lot of edge cases involved here.

Fix macvim-dev#1191
Part of Epic macvim-dev#1311, which contains other items to be implemented.
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