Skip to content

Commit

Permalink
feat(semanticTokens)!: token highlight groups (#4667)
Browse files Browse the repository at this point in the history
* feat(semanticTokens)!: token highlight groups

CocSemType + type
CocSemMod + modifier
CocSemTypeMod + type + modifier

The highlight group didn't need to be defined first

related #4659

* feat(semanticTokens)!: token highlight groups

CocSemType + type
CocSemTypeMod + type + modifier

* fix(highlight): check hlexists for vim

* feat(semanticTokens): rm modifiers in checkCurrent

modifiers are added to one token type, we can't list them directly

* feat(semanticTokens): follow tree-sitter's hi name

nvim-treesitter/nvim-treesitter#5895
nvim-treesitter/nvim-treesitter@1ae9b0e

* feat(highlight): coc_highlight_maximum_count 200
  • Loading branch information
fannheyward authored Mar 20, 2024
1 parent 8c85d6f commit b01ae44
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 108 deletions.
6 changes: 4 additions & 2 deletions autoload/coc/highlight.vim
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let s:namespace_map = {}
let s:ns_id = 1
let s:diagnostic_hlgroups = ['CocErrorHighlight', 'CocWarningHighlight', 'CocInfoHighlight', 'CocHintHighlight', 'CocDeprecatedHighlight', 'CocUnusedHighlight']
" Maximum count to highlight each time.
let g:coc_highlight_maximum_count = get(g:, 'coc_highlight_maximum_count', 100)
let g:coc_highlight_maximum_count = get(g:, 'coc_highlight_maximum_count', 200)
let s:term = &termguicolors == 0 && !has('gui_running')

if has('nvim-0.5.0') && s:clear_match_by_window == 0
Expand Down Expand Up @@ -319,7 +319,9 @@ function! coc#highlight#add_highlight(bufnr, src_id, hl_group, line, col_start,
call nvim_buf_add_highlight(a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end)
endif
else
call coc#api#exec('buf_add_highlight', [a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end, opts])
if hlexists(a:hl_group)
call coc#api#exec('buf_add_highlight', [a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end, opts])
endif
endif
endfunction

Expand Down
23 changes: 8 additions & 15 deletions doc/coc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1877,7 +1877,7 @@ g:coc_highlight_maximum_count *g:coc_highlight_maximum_count*
When highlight items exceed maximum count, highlight items will be
grouped and added by using |timer_start| for better user experience.

Default `100`
Default `200`

g:coc_default_semantic_highlight_groups *g:coc_default_semantic_highlight_groups*

Expand Down Expand Up @@ -3356,27 +3356,20 @@ Semantic highlight groups are starts with `CocSem` which link to related
highlight groups, use variable |g:coc_default_semantic_highlight_groups| to
disable creation of these highlight groups.

The highlight groups rules:
>
`CocSemType + type` for types
`CocSemTypeMode + type + modifier` for modifier with type
<

Only semantic tokens types and `deprecated` modifier have default
highlight groups.

You need create highlight groups for highlight other modifiers and/or specific
modifier with type, for example:
>
" Add highlights for defaultLibrary modifier
hi link CocSemDefaultLibrary TSOtherDefaultLibrary
hi link CocSemDefaultLibraryClass TSTypeDefaultLibrary
hi link CocSemDefaultLibraryInterface TSTypeDefaultLibrary
hi link CocSemDefaultLibraryEnum TSTypeDefaultLibrary
hi link CocSemDefaultLibraryType TSTypeDefaultLibrary
hi link CocSemDefaultLibraryNamespace TSTypeDefaultLibrary
" Add highlights for declaration modifier
hi link CocSemDeclaration TSOtherDeclaration
hi link CocSemDeclarationClass TSTypeDeclaration
hi link CocSemDeclarationInterface TSTypeDeclaration
hi link CocSemDeclarationEnum TSTypeDeclaration
hi link CocSemDeclarationType TSTypeDeclaration
hi link CocSemDeclarationNamespace TSTypeDeclaration
hi link CocSemTypeModClassDeclaration ClassDeclaration
<
The modifier highlight groups have higher priority.

Expand Down
7 changes: 7 additions & 0 deletions history.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 2024-02-28

- Increase `g:coc_highlight_maximum_count` default to 200
- Break change: semanticTokens highlight groups changed:
- `CocSem + type` to `CocSemType + type`
- `CocSem + modifier + type` to `CocSemTypeMod + type + modifier`

# 2024-03-06

- add `outline.autoHide` configuration to automatically hide the outline window when an item is clicked
Expand Down
56 changes: 28 additions & 28 deletions plugin/coc.vim
Original file line number Diff line number Diff line change
Expand Up @@ -562,31 +562,31 @@ function! s:Highlight() abort

if get(g:, 'coc_default_semantic_highlight_groups', 1)
let hlMap = {
\ 'Namespace': ['@namespace', 'Include'],
\ 'Type': ['@type', 'Type'],
\ 'Class': ['@constructor', 'Special'],
\ 'Enum': ['@type', 'Type'],
\ 'Interface': ['@type', 'Type'],
\ 'Struct': ['@structure', 'Identifier'],
\ 'TypeParameter': ['@parameter', 'Identifier'],
\ 'Parameter': ['@parameter', 'Identifier'],
\ 'Variable': ['@variable', 'Identifier'],
\ 'Property': ['@property', 'Identifier'],
\ 'EnumMember': ['@property', 'Constant'],
\ 'Event': ['@keyword', 'Keyword'],
\ 'Function': ['@function', 'Function'],
\ 'Method': ['@method', 'Function'],
\ 'Macro': ['@constant.macro', 'Define'],
\ 'Keyword': ['@keyword', 'Keyword'],
\ 'Modifier': ['@storageclass', 'StorageClass'],
\ 'Comment': ['@comment', 'Comment'],
\ 'String': ['@string', 'String'],
\ 'Number': ['@number', 'Number'],
\ 'Boolean': ['@boolean', 'Boolean'],
\ 'Regexp': ['@string.regex', 'String'],
\ 'Operator': ['@operator', 'Operator'],
\ 'Decorator': ['@symbol', 'Identifier'],
\ 'Deprecated': ['@text.strike', 'CocDeprecatedHighlight']
\ 'TypeNamespace': ['@module', 'Include'],
\ 'TypeType': ['@type', 'Type'],
\ 'TypeClass': ['@constructor', 'Special'],
\ 'TypeEnum': ['@type', 'Type'],
\ 'TypeInterface': ['@type', 'Type'],
\ 'TypeStruct': ['@structure', 'Identifier'],
\ 'TypeTypeParameter': ['@variable.parameter', 'Identifier'],
\ 'TypeParameter': ['@variable.parameter', 'Identifier'],
\ 'TypeVariable': ['@variable', 'Identifier'],
\ 'TypeProperty': ['@property', 'Identifier'],
\ 'TypeEnumMember': ['@property', 'Constant'],
\ 'TypeEvent': ['@keyword', 'Keyword'],
\ 'TypeFunction': ['@function', 'Function'],
\ 'TypeMethod': ['@function.method', 'Function'],
\ 'TypeMacro': ['@constant.macro', 'Define'],
\ 'TypeKeyword': ['@keyword', 'Keyword'],
\ 'TypeModifier': ['@keyword.storage', 'StorageClass'],
\ 'TypeComment': ['@comment', 'Comment'],
\ 'TypeString': ['@string', 'String'],
\ 'TypeNumber': ['@number', 'Number'],
\ 'TypeBoolean': ['@boolean', 'Boolean'],
\ 'TypeRegexp': ['@string.regexp', 'String'],
\ 'TypeOperator': ['@operator', 'Operator'],
\ 'TypeDecorator': ['@string.special.symbol', 'Identifier'],
\ 'ModDeprecated': ['@markup.strikethrough', 'CocDeprecatedHighlight']
\ }
for [key, value] in items(hlMap)
let ts = get(value, 0, '')
Expand All @@ -596,7 +596,7 @@ function! s:Highlight() abort
endif
let symbolMap = {
\ 'Keyword': ['@keyword', 'Keyword'],
\ 'Namespace': ['@namespace', 'Include'],
\ 'Namespace': ['@module', 'Include'],
\ 'Class': ['@constructor', 'Special'],
\ 'Method': ['@method', 'Function'],
\ 'Property': ['@property', 'Identifier'],
Expand All @@ -610,7 +610,7 @@ function! s:Highlight() abort
\ 'File': ['@file', 'Statement'],
\ 'Module': ['@module', 'Statement'],
\ 'Package': ['@package', 'Statement'],
\ 'Field': ['@field', 'Identifier'],
\ 'Field': ['@variable.member', 'Identifier'],
\ 'Constructor': ['@constructor', 'Special'],
\ 'Enum': ['@type', 'CocSymbolDefault'],
\ 'Interface': ['@type', 'CocSymbolDefault'],
Expand All @@ -628,7 +628,7 @@ function! s:Highlight() abort
\ 'Struct': ['@structure', 'Keyword'],
\ 'Event': ['@constant', 'Constant'],
\ 'Operator': ['@operator', 'Operator'],
\ 'TypeParameter': ['@parameter', 'Identifier'],
\ 'TypeParameter': ['@variable.parameter', 'Identifier'],
\ }
for [key, value] in items(symbolMap)
let hlGroup = coc#highlight#valid(value[0]) ? value[0] : get(value, 1, 'CocSymbolDefault')
Expand Down
12 changes: 2 additions & 10 deletions src/__tests__/handler/semanticTokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ describe('semanticTokens', () => {
}
}
}, { tokenModifiers: [], tokenTypes: [] }))
await semanticTokens.fetchHighlightGroups()
await semanticTokens.showHighlightInfo()
let buf = await nvim.buffer
let lines = await buf.lines
Expand Down Expand Up @@ -339,7 +338,7 @@ describe('semanticTokens', () => {
let buf = await win.buffer
let lines = await buf.lines
let content = lines.join('\n')
expect(content).toMatch('CocSemDeclarationFunction')
expect(content).toMatch('Type: function\nModifiers: declaration\nHighlight group: CocSemTypeFunction')
await window.moveTo({ line: 1, character: 0 })
await commandManager.executeCommand('semanticTokens.inspect')
win = await helper.getFloat()
Expand Down Expand Up @@ -717,7 +716,7 @@ describe('semanticTokens', () => {
await nvim.call('CocAction', 'semanticHighlight')
const highlights = await nvim.call("coc#highlight#get_highlights", [doc.bufnr, 'semanticTokens']) as any[]
expect(highlights.length).toBeGreaterThan(0)
expect(highlights[0][0]).toBe('CocSemKeyword')
expect(highlights[0][0]).toBe('CocSemTypeKeyword')
})
})

Expand Down Expand Up @@ -761,17 +760,10 @@ describe('semanticTokens', () => {
// @ts-ignore
workspace._env.updateHighlight = true
expect(enabled).toBe(false)
semanticTokens.staticConfig.highlightGroups = ['CocSemKeyword']
expect(() => {
item.checkState()
}).toThrow('provider not found')
registerProvider()
doc = await helper.createDocument('t.rs')
item = semanticTokens.getItem(doc.bufnr)
semanticTokens.staticConfig.highlightGroups = []
expect(() => {
item.checkState()
}).toThrow('Unable to find highlight groups')
})
})

Expand Down
49 changes: 19 additions & 30 deletions src/handler/semanticTokens/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ const highlightGroupMap: Map<string, string> = new Map()

export interface StaticConfig {
filetypes: string[] | null
highlightGroups: ReadonlyArray<string>
}

export default class SemanticTokensBuffer implements SyncItem {
Expand Down Expand Up @@ -190,7 +189,6 @@ export default class SemanticTokensBuffer implements SyncItem {
if (!workspace.env.updateHighlight) throw new Error(`Can't perform highlight update, highlight update requires vim >= 8.1.1719 or neovim >= 0.5.0`)
if (!this.configEnabled) throw new Error(`Semantic tokens highlight not enabled for current filetype: ${this.doc.filetype}`)
if (!this.hasProvider || !this.hasLegend) throw new Error(`SemanticTokens provider not found for ${this.doc.uri}`)
if (this.staticConfig.highlightGroups.length === 0) throw new Error(`Unable to find highlight groups starts with CocSem`)
}

public async getTokenRanges(
Expand Down Expand Up @@ -232,42 +230,33 @@ export default class SemanticTokensBuffer implements SyncItem {
* Single line only.
*/
private addHighlightItems(highlights: SemanticTokenRange[], range: [number, number, number], tokenType: string, tokenModifiers: string[]): void {
// highlight groups:
// CocSem + Type + type
// CocSem + TypeMod + type + modifier

let { combinedModifiers } = this.config
let { highlightGroups } = this.staticConfig
let highlightGroup: string
let combine = false
// Compose highlight group CocSem + modifier + type
for (let item of tokenModifiers) {
let hlGroup = HLGROUP_PREFIX + toHighlightPart(item) + toHighlightPart(tokenType)
if (highlightGroups.includes(hlGroup)) {
combine = combinedModifiers.includes(item)
highlightGroup = hlGroup
break
}
}
if (!highlightGroup) {
for (let modifier of tokenModifiers) {
let hlGroup = HLGROUP_PREFIX + toHighlightPart(modifier)
if (highlightGroups.includes(hlGroup)) {
highlightGroup = hlGroup
combine = combinedModifiers.includes(modifier)
break
}
}
}
if (!highlightGroup) {
let hlGroup = HLGROUP_PREFIX + toHighlightPart(tokenType)
if (highlightGroups.includes(hlGroup)) {
highlightGroup = hlGroup
}
}

highlights.push({
range,
tokenType,
combine,
hlGroup: highlightGroup,
hlGroup: HLGROUP_PREFIX + 'Type' + toHighlightPart(tokenType),
tokenModifiers,
})

if (tokenModifiers.length) {
// only use first modifier to avoid highlight flicking
const modifier = tokenModifiers[0]
combine = combinedModifiers.includes(modifier)
highlights.push({
range,
tokenType,
combine,
hlGroup: HLGROUP_PREFIX + 'TypeMod' + toHighlightPart(tokenType) + toHighlightPart(modifier),
tokenModifiers,
})
}
}

private toHighlightItems(highlights: ReadonlyArray<SemanticTokenRange>, startLine?: number, endLine?: number): HighlightItem[] {
Expand Down
27 changes: 4 additions & 23 deletions src/handler/semanticTokens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import BufferSync from '../../model/bufferSync'
import Highlighter from '../../model/highlighter'
import { Documentation, FloatFactory } from '../../types'
import { disposeAll } from '../../util'
import { distinct, isFalsyOrEmpty, toArray } from '../../util/array'
import { distinct, toArray } from '../../util/array'
import type { Disposable } from '../../util/protocol'
import { toErrorText, toText, upperFirst } from '../../util/string'
import { toErrorText, toText } from '../../util/string'
import window from '../../window'
import workspace from '../../workspace'
import SemanticTokensBuffer, { HLGROUP_PREFIX, NAMESPACE, StaticConfig, toHighlightPart } from './buffer'
Expand All @@ -28,7 +28,6 @@ export default class SemanticTokens {
constructor(private nvim: Neovim) {
this.staticConfig = {
filetypes: getFiletypes(),
highlightGroups: []
}
workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('semanticTokens')) {
Expand Down Expand Up @@ -76,7 +75,6 @@ export default class SemanticTokens {
return new SemanticTokensBuffer(this.nvim, doc, this.staticConfig)
})
languages.onDidSemanticTokensRefresh(async selector => {
if (isFalsyOrEmpty(this.staticConfig.highlightGroups)) await this.fetchHighlightGroups()
let visibleBufs = window.visibleTextEditors.map(o => o.document.bufnr)
for (let item of this.highlighters.items) {
if (!workspace.match(selector, item.doc)) continue
Expand Down Expand Up @@ -157,11 +155,6 @@ export default class SemanticTokens {
floatFactory?.close()
}

public async fetchHighlightGroups(): Promise<void> {
let highlightGroups = await this.nvim.call('coc#util#semantic_hlgroups') as string[]
this.staticConfig.highlightGroups = highlightGroups
}

public async getCurrentItem(): Promise<SemanticTokensBuffer | undefined> {
let buf = await this.nvim.buffer
return this.getItem(buf.id)
Expand All @@ -176,7 +169,6 @@ export default class SemanticTokens {
public async highlightCurrent(): Promise<void> {
let item = await this.getCurrentItem()
if (!item || !item.enabled) throw new Error(`Unable to perform semantic highlights for current buffer.`)
await this.fetchHighlightGroups()
await item.forceHighlight()
}

Expand Down Expand Up @@ -215,26 +207,15 @@ export default class SemanticTokens {
let legend = languages.getLegend(doc.textDocument) ?? languages.getLegend(doc.textDocument, true)
if (legend.tokenTypes.length) {
for (const t of [...new Set(legend.tokenTypes)]) {
let text = HLGROUP_PREFIX + toHighlightPart(t)
let text = HLGROUP_PREFIX + 'Type' + toHighlightPart(t)
hl.addTexts([{ text: '-', hlGroup: 'Comment' }, { text: ' ' }, { text, hlGroup: text }])
}
hl.addLine('')
} else {
hl.addLine('No token types supported', 'Comment')
hl.addLine('')
}
hl.addLine('Tokens modifiers that current Language Server supported:', headGroup)
hl.addLine('')
if (legend.tokenModifiers.length) {
for (const t of [...new Set(legend.tokenModifiers)]) {
let text = HLGROUP_PREFIX + toHighlightPart(t)
hl.addTexts([{ text: '-', hlGroup: 'Comment' }, { text: ' ' }, { text, hlGroup: text }])
}
hl.addLine('')
} else {
hl.addLine('No token modifiers exist', 'Comment')
hl.addLine('')
}
// modifiers are added to one token type, we can't list them directly
} catch (e) {
hl.addLine(toErrorText(e))
}
Expand Down

0 comments on commit b01ae44

Please sign in to comment.