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

allow to set semantic highlight groups based on filetype #4167

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
113 changes: 81 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,43 +936,92 @@ per buffer, by setting `b:ycm_enable_semantic_highlighting`.
#### Customising the highlight groups

YCM uses text properties (see `:help text-prop-intro`) for semantic
highlighting. In order to customise the coloring, you can define the text
properties that are used.

If you define a text property named `YCM_HL_<token type>`, then it will be used
in place of the defaults. The `<token type>` is defined as the Language Server
Protocol semantic token type, defined in the [LSP Spec](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens).
highlighting. In order to customise the coloring, you should set
`g:ycm_semantic_highlight_groups` list. Each item in that list must be
dictionary with next fields:
- `filetypes` - list of filetypes that should use this settings for semantic
highlighting. If not defined, then this settings will be used as default for
any not defined filetype
- `highlight` - dictionary, where key is [token type](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens)
and value is highlighting group. If not defined, then semantic highlighting
will be disabled for that filetypes. If group is empty string or `v:null`,
then semantic highlighing for that group will be disabled

Some servers also use custom values. In this case, YCM prints a warning
including the token type name that you can customise.

For example, to render `parameter` tokens using the `Normal` highlight group,
you can do this:

```viml
call prop_type_add( 'YCM_HL_parameter', { 'highlight': 'Normal' } )
```

More generally, this pattern can be useful for customising the groups:

```viml
let MY_YCM_HIGHLIGHT_GROUP = {
\ 'typeParameter': 'PreProc',
\ 'parameter': 'Normal',
\ 'variable': 'Normal',
\ 'property': 'Normal',
\ 'enumMember': 'Normal',
\ 'event': 'Special',
\ 'member': 'Normal',
\ 'method': 'Normal',
\ 'class': 'Special',
\ 'namespace': 'Special',
For example (set custom highlight for go, disable highlight for rust and define
default highlight for other filetypes):

```viml
let g:ycm_enable_semantic_highlighting=1

let g:ycm_semantic_highlight_groups = [
\{
\ 'filetypes': ['go'],
\ 'highlight': {
\ 'namespace': 'Namespace',
\ 'type': 'goType',
\ 'class': 'goType',
\ 'struct': 'goType',
\ 'interface': 'goType',
\ 'concept': v:null,
\ 'typeParameter': v:null,
\ 'enum': 'EnumConstant',
\ 'enumMember': 'EnumConstant',
\ 'function': 'goFunction',
\ 'method': 'goFunction',
\ 'member': 'goFunction',
\ 'property': 'goFunction',
\ 'macro': v:null,
\ 'variable': v:null,
\ 'parameter': v:null,
\ 'comment': v:null,
\ 'operator': v:null,
\ 'keyword': v:null,
\ 'modifier': v:null,
\ 'event': v:null,
\ 'number': v:null,
\ 'string': v:null,
\ 'regexp': v:null,
\ 'unknown': v:null,
\ 'bracket': v:null,
\ }

for tokenType in keys( MY_YCM_HIGHLIGHT_GROUP )
call prop_type_add( 'YCM_HL_' . tokenType,
\ { 'highlight': MY_YCM_HIGHLIGHT_GROUP[ tokenType ] } )
endfor
\},
\{
\ 'filetypes': ['rust']
\ }
\},
\{
\ 'highlight': {
\ 'namespace': 'Type',
\ 'type': 'Type',
\ 'class': 'Structure',
\ 'enum': 'Structure',
\ 'interface': 'Structure',
\ 'struct': 'Structure',
\ 'typeParameter': 'Identifier',
\ 'parameter': 'Identifier',
\ 'variable': 'Identifier',
\ 'property': 'Identifier',
\ 'enumMember': 'Identifier',
\ 'enumConstant': 'Constant',
\ 'event': 'Identifier',
\ 'function': 'Function',
\ 'member': 'Identifier',
\ 'macro': 'Macro',
\ 'method': 'Function',
\ 'keyword': 'Keyword',
\ 'modifier': 'Keyword',
\ 'comment': 'Comment',
\ 'string': 'String',
\ 'number': 'Number',
\ 'regexp': 'String',
\ 'operator': 'Operator',
\ 'unknown': 'Normal',
\ }
\}
\]
```

## Inlay hints
Expand Down
157 changes: 116 additions & 41 deletions python/ycm/semantic_highlighting.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,53 +25,98 @@
import vim


HIGHLIGHT_GROUP = {
'namespace': 'Type',
'type': 'Type',
'class': 'Structure',
'enum': 'Structure',
'interface': 'Structure',
'struct': 'Structure',
'typeParameter': 'Identifier',
'parameter': 'Identifier',
'variable': 'Identifier',
'property': 'Identifier',
'enumMember': 'Identifier',
'enumConstant': 'Constant',
'event': 'Identifier',
'function': 'Function',
'member': 'Identifier',
'macro': 'Macro',
'method': 'Function',
'keyword': 'Keyword',
'modifier': 'Keyword',
'comment': 'Comment',
'string': 'String',
'number': 'Number',
'regexp': 'String',
'operator': 'Operator',
'unknown': 'Normal',
}
HIGHLIGHT_GROUPS = [{
'highlight': {
'namespace': 'Type',
'type': 'Type',
'class': 'Structure',
'enum': 'Structure',
'interface': 'Structure',
'struct': 'Structure',
'typeParameter': 'Identifier',
'parameter': 'Identifier',
'variable': 'Identifier',
'property': 'Identifier',
'enumMember': 'Identifier',
'enumConstant': 'Constant',
'event': 'Identifier',
'function': 'Function',
'member': 'Identifier',
'macro': 'Macro',
'method': 'Function',
'keyword': 'Keyword',
'modifier': 'Keyword',
'comment': 'Comment',
'string': 'String',
'number': 'Number',
'regexp': 'String',
'operator': 'Operator',
'unknown': 'Normal',
}
}]
REPORTED_MISSING_TYPES = set()


def AddHiForTokenType( bufnr, token_type, group ):
prop = f'YCM_HL_{ token_type }'
hi = group
combine = 0
filetypes = "(default)"
if bufnr is not None:
filetypes = vimsupport.GetBufferFiletypes(bufnr)

Check warning on line 66 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L66

Added line #L66 was not covered by tests

if group is None or len( group ) == 0:
hi = 'Normal'
combine = 1

Check warning on line 70 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L69-L70

Added lines #L69 - L70 were not covered by tests

if not vimsupport.GetIntValue(
f"hlexists( '{ vimsupport.EscapeForVim( hi ) }' )" ):
vimsupport.PostVimMessage(

Check warning on line 74 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L74

Added line #L74 was not covered by tests
f"Higlight group { hi } is not difined for { filetypes }. "
f"See :help youcompleteme-customising-highlight-groups" )
return

Check warning on line 77 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L77

Added line #L77 was not covered by tests

if bufnr is None:
props = tp.GetTextPropertyTypes()
if prop not in props:
tp.AddTextPropertyType( prop,
highlight = hi,
priority = 0,
combine = combine )
else:
tp.AddTextPropertyType( prop,

Check warning on line 87 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L87

Added line #L87 was not covered by tests
highlight = hi,
priority = 0,
combine = combine,
bufnr = bufnr )


def Initialise():
if vimsupport.VimIsNeovim():
return

props = tp.GetTextPropertyTypes()
if 'YCM_HL_UNKNOWN' not in props:
tp.AddTextPropertyType( 'YCM_HL_UNKNOWN',
highlight = 'WarningMsg',
priority = 0 )
global HIGHLIGHT_GROUPS

for token_type, group in HIGHLIGHT_GROUP.items():
prop = f'YCM_HL_{ token_type }'
if prop not in props and vimsupport.GetIntValue(
f"hlexists( '{ vimsupport.EscapeForVim( group ) }' )" ):
tp.AddTextPropertyType( prop,
highlight = group,
priority = 0 )
if "ycm_semantic_highlight_groups" in vimsupport.GetVimGlobalsKeys():
hi_groups: list[dict] = vimsupport.VimExpressionToPythonType(

Check warning on line 101 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L101

Added line #L101 was not covered by tests
"g:ycm_semantic_highlight_groups" )
hi_groups.extend( HIGHLIGHT_GROUPS[:] )
HIGHLIGHT_GROUPS = hi_groups

Check warning on line 104 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L103-L104

Added lines #L103 - L104 were not covered by tests

# init default highlight
default_hi = None
for groups in HIGHLIGHT_GROUPS:
if 'filetypes' not in groups:
default_hi = groups
break

if default_hi is None or 'highlight' not in default_hi:
return

Check warning on line 114 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L114

Added line #L114 was not covered by tests

# XXX define default settings globally for make it compatible with older
# settings
for token_type, group in default_hi[ 'highlight' ].items():
AddHiForTokenType( None, token_type, group )


# "arbitrary" base id
Expand All @@ -94,6 +139,34 @@
self._prop_id = NextPropID()
super().__init__( bufnr )

self._filetypes = vimsupport.GetBufferFiletypes( bufnr )

default_hi = None
target_groups = None
for ft_groups in HIGHLIGHT_GROUPS:
if 'filetypes' in ft_groups:
for filetype in self._filetypes:
if filetype in ft_groups[ 'filetypes' ]:
target_groups = ft_groups

Check warning on line 150 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L148-L150

Added lines #L148 - L150 were not covered by tests
elif default_hi is None:
default_hi = ft_groups

if target_groups is None and ( default_hi is None or 'highlight' not in default_hi ):
self._do_highlight = False
return

Check warning on line 156 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L155-L156

Added lines #L155 - L156 were not covered by tests
elif target_groups is None:
# default highlight should be defined globaly
self._do_highlight = True
return
elif 'highlight' not in target_groups:
self._do_highlight = False
return

Check warning on line 163 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L161-L163

Added lines #L161 - L163 were not covered by tests

for token_type, group in target_groups[ 'highlight' ].items():
AddHiForTokenType( bufnr, token_type, group )

Check warning on line 166 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L165-L166

Added lines #L165 - L166 were not covered by tests

self._do_highlight = True

Check warning on line 168 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L168

Added line #L168 was not covered by tests


def _NewRequest( self, request_range ):
request: dict = BuildRequestData( self._bufnr )
Expand All @@ -102,6 +175,9 @@


def _Draw( self ):
if self._do_highlight == False:
return

Check warning on line 179 in python/ycm/semantic_highlighting.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/semantic_highlighting.py#L179

Added line #L179 was not covered by tests

# We requested a snapshot
tokens = self._latest_response.get( 'tokens', [] )

Expand All @@ -120,8 +196,7 @@
if token[ 'type' ] not in REPORTED_MISSING_TYPES:
REPORTED_MISSING_TYPES.add( token[ 'type' ] )
vimsupport.PostVimMessage(
f"Token type { token[ 'type' ] } not supported. "
f"Define property type { prop_type }. "
f"Token type { token[ 'type' ] } is not defined for { self._filetypes }. "
f"See :help youcompleteme-customising-highlight-groups" )
else:
raise e
Expand Down