First version
This commit is contained in:
3
after/ftplugin/go.lua
Normal file
3
after/ftplugin/go.lua
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
local go = require('lgwtdark.go_extra')
|
||||||
|
go.setup()
|
||||||
|
go.attach(vim.api.nvim_get_current_buf())
|
||||||
31
after/queries/go/highlights.scm
Normal file
31
after/queries/go/highlights.scm
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
;; lgwtdark: split string bodies vs delimiters (delim white via extmarks in go_extra.lua).
|
||||||
|
;; Lower parent literal ranges so inner captures win.
|
||||||
|
|
||||||
|
((interpreted_string_literal) @string
|
||||||
|
(#set! priority 95))
|
||||||
|
|
||||||
|
((interpreted_string_literal_content) @string
|
||||||
|
(#set! priority 115))
|
||||||
|
|
||||||
|
((escape_sequence) @string.escape
|
||||||
|
(#set! priority 115))
|
||||||
|
|
||||||
|
((raw_string_literal) @string
|
||||||
|
(#set! priority 95))
|
||||||
|
|
||||||
|
((raw_string_literal_content) @string
|
||||||
|
(#set! priority 115))
|
||||||
|
|
||||||
|
;; rune 'x' is one token — quotes/inner in go_extra.lua
|
||||||
|
((rune_literal) @string
|
||||||
|
(#set! priority 88))
|
||||||
|
|
||||||
|
;; Site: nil / booleans read as values (warm), not like keywords. iota stays default @constant.builtin.
|
||||||
|
((nil) @number
|
||||||
|
(#set! priority 128))
|
||||||
|
|
||||||
|
((true) @number
|
||||||
|
(#set! priority 128))
|
||||||
|
|
||||||
|
((false) @number
|
||||||
|
(#set! priority 128))
|
||||||
234
colors/lgwtdark.lua
Normal file
234
colors/lgwtdark.lua
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
-- Learn Go With Tests (dark) - palette from site CSS, dark-mode token colors.
|
||||||
|
-- Requires: termguicolors. For Go, nvim-treesitter is strongly recommended.
|
||||||
|
|
||||||
|
vim.cmd.hi('clear')
|
||||||
|
vim.g.colors_name = 'lgwtdark'
|
||||||
|
|
||||||
|
if vim.fn.exists('syntax_on') == 1 then
|
||||||
|
vim.cmd.syntax('reset')
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.o.termguicolors = true
|
||||||
|
vim.o.background = 'dark'
|
||||||
|
|
||||||
|
-- Dark-mode values from your spec (second branch of light-dark / --shiki-dark).
|
||||||
|
local C = {
|
||||||
|
-- primary-2: code panels on the site read slightly cooler than primary-1; warm text pops closer to the book.
|
||||||
|
bg = '#202325',
|
||||||
|
keyword = '#f8a8a1', -- danger-11
|
||||||
|
string_ = '#9bd0a1', -- success-11
|
||||||
|
entity = '#a0c6e0', -- primary-11 (packages, types, func/method names & calls)
|
||||||
|
-- warning-11 nudged warmer (more R, less B) so it matches the orange-gold on the page next to this bg
|
||||||
|
warm = '#e5b899',
|
||||||
|
fg = '#feffff', -- tint-12 (variables, brackets, delimiters)
|
||||||
|
comment = '#787878', -- neutral-9
|
||||||
|
operator = '#bdc1c3', -- tint-11
|
||||||
|
-- subtle UI derived from primary scale
|
||||||
|
cursorline = '#262d32', -- primary-3
|
||||||
|
visual = '#34495a', -- between primary-4 / 5
|
||||||
|
linenr = '#525252', -- neutral-7
|
||||||
|
float_bg = '#222222',
|
||||||
|
}
|
||||||
|
|
||||||
|
local function hi(name, opts)
|
||||||
|
vim.api.nvim_set_hl(0, name, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Editor chrome
|
||||||
|
hi('Normal', { fg = C.fg, bg = C.bg })
|
||||||
|
hi('NormalNC', { fg = C.fg, bg = C.bg })
|
||||||
|
hi('NormalFloat', { fg = C.fg, bg = C.float_bg })
|
||||||
|
hi('Cursor', { fg = C.bg, bg = C.entity })
|
||||||
|
hi('lCursor', { link = 'Cursor' })
|
||||||
|
hi('CursorLine', { bg = C.cursorline })
|
||||||
|
hi('CursorColumn', { bg = C.cursorline })
|
||||||
|
hi('LineNr', { fg = C.linenr, bg = C.bg })
|
||||||
|
hi('CursorLineNr', { fg = C.warm, bg = C.cursorline })
|
||||||
|
hi('SignColumn', { bg = C.bg })
|
||||||
|
hi('Folded', { fg = C.comment, bg = '#242424' })
|
||||||
|
hi('FoldColumn', { fg = C.comment, bg = C.bg })
|
||||||
|
hi('Whitespace', { fg = '#333333' })
|
||||||
|
hi('WinSeparator', { fg = '#2a2a2a', bg = C.bg })
|
||||||
|
hi('EndOfBuffer', { fg = C.bg, bg = C.bg })
|
||||||
|
|
||||||
|
hi('Search', { fg = C.bg, bg = C.warm })
|
||||||
|
hi('IncSearch', { fg = C.bg, bg = C.keyword })
|
||||||
|
hi('Visual', { bg = C.visual })
|
||||||
|
hi('VisualNOS', { bg = C.visual })
|
||||||
|
|
||||||
|
hi('StatusLine', { fg = C.fg, bg = '#333333' })
|
||||||
|
hi('StatusLineNC', { fg = C.comment, bg = '#252525' })
|
||||||
|
hi('TabLine', { fg = C.comment, bg = '#252525' })
|
||||||
|
hi('TabLineFill', { bg = C.bg })
|
||||||
|
hi('TabLineSel', { fg = C.entity, bg = '#333333' })
|
||||||
|
|
||||||
|
hi('Error', { fg = '#fb2c36', bold = true })
|
||||||
|
hi('WarningMsg', { fg = '#fe9a00' })
|
||||||
|
hi('MatchParen', { fg = C.keyword, bg = '#3a3a3a', bold = true })
|
||||||
|
|
||||||
|
hi('Pmenu', { fg = C.fg, bg = '#2d2d2d' })
|
||||||
|
hi('PmenuSel', { fg = C.bg, bg = C.entity })
|
||||||
|
hi('PmenuSbar', { bg = '#444444' })
|
||||||
|
hi('PmenuThumb', { bg = '#666666' })
|
||||||
|
|
||||||
|
hi('Title', { fg = C.entity, bold = true })
|
||||||
|
hi('Directory', { fg = C.entity })
|
||||||
|
|
||||||
|
-- Builtin highlight groups (legacy syntax / fallbacks)
|
||||||
|
hi('Comment', { fg = C.comment })
|
||||||
|
hi('String', { fg = C.string_ })
|
||||||
|
hi('Character', { fg = C.string_ })
|
||||||
|
hi('Number', { fg = C.warm })
|
||||||
|
hi('Float', { fg = C.warm })
|
||||||
|
hi('Boolean', { fg = C.warm })
|
||||||
|
hi('Constant', { fg = C.warm })
|
||||||
|
|
||||||
|
hi('Identifier', { fg = C.fg })
|
||||||
|
hi('Function', { fg = C.entity })
|
||||||
|
hi('Statement', { fg = C.keyword })
|
||||||
|
hi('Conditional', { fg = C.keyword })
|
||||||
|
hi('Repeat', { fg = C.keyword })
|
||||||
|
hi('Label', { fg = C.keyword })
|
||||||
|
hi('Operator', { fg = C.operator })
|
||||||
|
hi('Keyword', { fg = C.keyword })
|
||||||
|
hi('Exception', { fg = C.keyword })
|
||||||
|
hi('PreProc', { fg = C.keyword })
|
||||||
|
hi('Include', { fg = C.keyword })
|
||||||
|
hi('Define', { fg = C.keyword })
|
||||||
|
hi('Macro', { fg = C.entity })
|
||||||
|
hi('Type', { fg = C.entity })
|
||||||
|
hi('StorageClass', { fg = C.keyword })
|
||||||
|
hi('Structure', { fg = C.keyword })
|
||||||
|
hi('Special', { fg = C.warm })
|
||||||
|
hi('SpecialChar', { fg = C.warm })
|
||||||
|
hi('Delimiter', { fg = C.fg })
|
||||||
|
hi('Underlined', { fg = C.entity, underline = true })
|
||||||
|
hi('Ignore', { fg = C.bg })
|
||||||
|
hi('Todo', { fg = C.warm, bold = true })
|
||||||
|
|
||||||
|
-- Treesitter (Go and general)
|
||||||
|
local ts = {
|
||||||
|
['@comment'] = { fg = C.comment },
|
||||||
|
['@comment.documentation'] = { fg = C.comment },
|
||||||
|
|
||||||
|
['@keyword'] = { fg = C.keyword },
|
||||||
|
['@keyword.function'] = { fg = C.keyword },
|
||||||
|
['@keyword.operator'] = { fg = C.keyword },
|
||||||
|
['@keyword.return'] = { fg = C.keyword },
|
||||||
|
['@keyword.import'] = { fg = C.keyword },
|
||||||
|
['@keyword.exception'] = { fg = C.keyword },
|
||||||
|
['@keyword.coroutine'] = { fg = C.keyword },
|
||||||
|
['@keyword.directive'] = { fg = C.keyword },
|
||||||
|
['@keyword.directive.define'] = { fg = C.keyword },
|
||||||
|
|
||||||
|
['@type.builtin'] = { fg = C.keyword },
|
||||||
|
|
||||||
|
['@string'] = { fg = C.string_ },
|
||||||
|
['@string.documentation'] = { fg = C.string_ },
|
||||||
|
['@string.regex'] = { fg = C.string_ },
|
||||||
|
['@string.escape'] = { fg = C.warm },
|
||||||
|
['@string.special'] = { fg = C.warm },
|
||||||
|
['@string.special.url'] = { fg = C.entity, underline = true },
|
||||||
|
['@character'] = { fg = C.string_ },
|
||||||
|
['@character.special'] = { fg = C.warm },
|
||||||
|
|
||||||
|
['@number'] = { fg = C.warm },
|
||||||
|
['@float'] = { fg = C.warm },
|
||||||
|
['@boolean'] = { fg = C.warm },
|
||||||
|
|
||||||
|
['@function'] = { fg = C.entity },
|
||||||
|
['@function.call'] = { fg = C.entity },
|
||||||
|
['@function.builtin'] = { fg = C.entity },
|
||||||
|
['@function.macro'] = { fg = C.entity },
|
||||||
|
|
||||||
|
['@method'] = { fg = C.entity },
|
||||||
|
['@method.call'] = { fg = C.entity },
|
||||||
|
|
||||||
|
['@constructor'] = { fg = C.entity },
|
||||||
|
|
||||||
|
['@type'] = { fg = C.entity },
|
||||||
|
['@type.definition'] = { fg = C.entity },
|
||||||
|
|
||||||
|
['@namespace'] = { fg = C.entity },
|
||||||
|
['@module'] = { fg = C.entity },
|
||||||
|
|
||||||
|
['@variable'] = { fg = C.fg },
|
||||||
|
['@variable.builtin'] = { fg = C.keyword },
|
||||||
|
['@variable.parameter'] = { fg = C.warm },
|
||||||
|
['@variable.member'] = { fg = C.fg },
|
||||||
|
|
||||||
|
['@parameter'] = { fg = C.warm },
|
||||||
|
|
||||||
|
['@constant'] = { fg = C.warm },
|
||||||
|
['@constant.builtin'] = { fg = C.keyword },
|
||||||
|
['@constant.macro'] = { fg = C.entity },
|
||||||
|
|
||||||
|
['@property'] = { fg = C.fg },
|
||||||
|
['@field'] = { fg = C.fg },
|
||||||
|
|
||||||
|
['@operator'] = { fg = C.operator },
|
||||||
|
|
||||||
|
['@punctuation.delimiter'] = { fg = C.fg },
|
||||||
|
['@punctuation.bracket'] = { fg = C.fg },
|
||||||
|
['@punctuation.special'] = { fg = C.fg },
|
||||||
|
|
||||||
|
['@label'] = { fg = C.warm },
|
||||||
|
['@exception'] = { fg = C.keyword },
|
||||||
|
['@include'] = { fg = C.keyword },
|
||||||
|
['@repeat'] = { fg = C.keyword },
|
||||||
|
['@conditional'] = { fg = C.keyword },
|
||||||
|
|
||||||
|
['@tag'] = { fg = C.entity },
|
||||||
|
['@tag.attribute'] = { fg = C.warm },
|
||||||
|
['@tag.delimiter'] = { fg = C.fg },
|
||||||
|
|
||||||
|
['@markup.link'] = { fg = C.entity, underline = true },
|
||||||
|
['@markup.link.url'] = { fg = C.entity, underline = true },
|
||||||
|
['@markup.strong'] = { bold = true },
|
||||||
|
['@markup.italic'] = { italic = true },
|
||||||
|
}
|
||||||
|
|
||||||
|
for g, o in pairs(ts) do
|
||||||
|
hi(g, o)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Diagnostics (readable on this bg)
|
||||||
|
hi('DiagnosticError', { fg = '#fb2c36' })
|
||||||
|
hi('DiagnosticWarn', { fg = '#fe9a00' })
|
||||||
|
hi('DiagnosticInfo', { fg = C.entity })
|
||||||
|
hi('DiagnosticHint', { fg = C.comment })
|
||||||
|
hi('DiagnosticUnderlineError', { sp = '#fb2c36', undercurl = true })
|
||||||
|
hi('DiagnosticUnderlineWarn', { sp = '#fe9a00', undercurl = true })
|
||||||
|
hi('DiagnosticUnderlineInfo', { sp = C.entity, undercurl = true })
|
||||||
|
hi('DiagnosticUnderlineHint', { sp = C.comment, undercurl = true })
|
||||||
|
|
||||||
|
-- LSP semantic tokens → same story as Treesitter where possible
|
||||||
|
hi('@lsp.type.namespace', { link = '@module' })
|
||||||
|
hi('@lsp.type.type', { link = '@type' })
|
||||||
|
hi('@lsp.type.class', { link = '@type' })
|
||||||
|
hi('@lsp.type.enum', { link = '@type' })
|
||||||
|
hi('@lsp.type.interface', { link = '@type' })
|
||||||
|
hi('@lsp.type.struct', { link = '@type' })
|
||||||
|
hi('@lsp.type.typeParameter', { link = '@type' })
|
||||||
|
hi('@lsp.type.function', { link = '@function' })
|
||||||
|
hi('@lsp.type.method', { link = '@method' })
|
||||||
|
hi('@lsp.type.property', { link = '@property' })
|
||||||
|
hi('@lsp.type.variable', { link = '@variable' })
|
||||||
|
hi('@lsp.type.parameter', { link = '@parameter' })
|
||||||
|
hi('@lsp.type.enumMember', { link = '@constant' })
|
||||||
|
hi('@lsp.type.decorator', { link = '@function.macro' })
|
||||||
|
hi('@lsp.type.macro', { link = '@function.macro' })
|
||||||
|
hi('@lsp.type.keyword', { link = '@keyword' })
|
||||||
|
hi('@lsp.type.operator', { link = '@operator' })
|
||||||
|
hi('@lsp.type.string', { link = '@string' })
|
||||||
|
hi('@lsp.type.number', { link = '@number' })
|
||||||
|
hi('@lsp.type.regexp', { link = '@string.regex' })
|
||||||
|
hi('@lsp.mod.readonly', { link = '@constant' })
|
||||||
|
hi('@lsp.mod.defaultLibrary', { link = '@type.builtin' })
|
||||||
|
|
||||||
|
-- Extmarks from lua/lgwtdark/go_extra.lua (fmt verbs, rune delimiters)
|
||||||
|
hi('LgwtdFmtVerb', { fg = C.warm })
|
||||||
|
hi('LgwtdQuote', { fg = C.fg })
|
||||||
|
hi('LgwtdStrInner', { fg = C.string_ })
|
||||||
|
hi('LgwtdEscape', { fg = C.warm })
|
||||||
|
hi('LgwtdNil', { link = '@number' })
|
||||||
29
init.lua
Normal file
29
init.lua
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
vim.g.mapleader = ' '
|
||||||
|
vim.g.maplocalleader = ' '
|
||||||
|
|
||||||
|
require('user.cursor')
|
||||||
|
require('user.keymaps')
|
||||||
|
require('user.options')
|
||||||
|
|
||||||
|
vim.opt.termguicolors = true
|
||||||
|
|
||||||
|
-- Plugin manager: https://github.com/folke/lazy.nvim
|
||||||
|
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
|
||||||
|
if not (vim.uv or vim.loop).fs_stat(lazypath) then
|
||||||
|
vim.fn.system({
|
||||||
|
'git',
|
||||||
|
'clone',
|
||||||
|
'--filter=blob:none',
|
||||||
|
'https://github.com/folke/lazy.nvim.git',
|
||||||
|
'--branch=stable',
|
||||||
|
lazypath,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
vim.opt.rtp:prepend(lazypath)
|
||||||
|
|
||||||
|
require('lazy').setup('plugins', {
|
||||||
|
install = { colorscheme = { 'lgwtdark', 'habamax' } },
|
||||||
|
checker = { enabled = false },
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.cmd.colorscheme('lgwtdark')
|
||||||
4
lazy-lock.json
Normal file
4
lazy-lock.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
||||||
|
"nvim-treesitter": { "branch": "master", "commit": "cf12346a3414fa1b06af75c79faebe7f76df080a" }
|
||||||
|
}
|
||||||
374
lua/lgwtdark/go_extra.lua
Normal file
374
lua/lgwtdark/go_extra.lua
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
-- Extra highlights Tree-sitter cannot express alone:
|
||||||
|
-- 1) fmt verbs (%q, %s, ...) inside interpreted strings -> warm (same as numbers/params)
|
||||||
|
-- 2) rune literals: single token — paint ' delimiters and inner body
|
||||||
|
-- 3) interpreted " and raw ` delimiters white (TS anonymous quotes are unreliable)
|
||||||
|
-- 4) comment // and /* */ edge markers white
|
||||||
|
--
|
||||||
|
-- Note: pointer `*` vertical alignment vs letters is controlled by the font/terminal,
|
||||||
|
-- not Neovim highlights. Try a font with a centered asterisk (e.g. Iosevka, JetBrains Mono).
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local ns = vim.api.nvim_create_namespace('lgwtdark_go')
|
||||||
|
|
||||||
|
-- package fmt / fmt.Errorf verbs (trailing character; %% is handled separately)
|
||||||
|
local FMT_VERB = {
|
||||||
|
['%'] = true,
|
||||||
|
b = true,
|
||||||
|
c = true,
|
||||||
|
d = true,
|
||||||
|
e = true,
|
||||||
|
E = true,
|
||||||
|
f = true,
|
||||||
|
F = true,
|
||||||
|
g = true,
|
||||||
|
G = true,
|
||||||
|
h = true,
|
||||||
|
o = true,
|
||||||
|
O = true,
|
||||||
|
p = true,
|
||||||
|
q = true,
|
||||||
|
s = true,
|
||||||
|
t = true,
|
||||||
|
T = true,
|
||||||
|
U = true,
|
||||||
|
v = true,
|
||||||
|
V = true,
|
||||||
|
w = true,
|
||||||
|
x = true,
|
||||||
|
X = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Return inclusive [start, end] byte indices in `s` for each fmt verb span (package fmt style).
|
||||||
|
local function fmt_verb_spans(s)
|
||||||
|
local spans = {}
|
||||||
|
local i = 1
|
||||||
|
local n = #s
|
||||||
|
while i <= n do
|
||||||
|
if s:byte(i) ~= 37 then -- %
|
||||||
|
i = i + 1
|
||||||
|
elseif i < n and s:byte(i + 1) == 37 then
|
||||||
|
i = i + 2 -- %%
|
||||||
|
else
|
||||||
|
local j = i + 1
|
||||||
|
if j <= n and s:byte(j) == 91 then -- %[n]
|
||||||
|
local close = s:find('%]', j + 1, true)
|
||||||
|
if not close then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
j = close + 1
|
||||||
|
end
|
||||||
|
while j <= n do
|
||||||
|
local c = s:byte(j)
|
||||||
|
if c == 35 or c == 43 or c == 45 or c == 32 or c == 48 then -- # + - space 0
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if j <= n and s:byte(j) == 42 then
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
while j <= n do
|
||||||
|
local c = s:byte(j)
|
||||||
|
if c >= 48 and c <= 57 then
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if j <= n and s:byte(j) == 46 then
|
||||||
|
j = j + 1
|
||||||
|
if j <= n and s:byte(j) == 42 then
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
while j <= n do
|
||||||
|
local c = s:byte(j)
|
||||||
|
if c >= 48 and c <= 57 then
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if j <= n then
|
||||||
|
local ch = s:sub(j, j)
|
||||||
|
if FMT_VERB[ch] then
|
||||||
|
spans[#spans + 1] = { i, j }
|
||||||
|
i = j + 1
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return spans
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clear_buf(bufnr)
|
||||||
|
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function extmark_verb(bufnr, row, col_base, text, a, b)
|
||||||
|
local start_col = col_base + #text:sub(1, a - 1)
|
||||||
|
local end_col = col_base + #text:sub(1, b)
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, row, start_col, {
|
||||||
|
end_col = end_col,
|
||||||
|
hl_group = 'LgwtdFmtVerb',
|
||||||
|
priority = 200,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hl_interpreted_double_quotes(bufnr, node)
|
||||||
|
local text = vim.treesitter.get_node_text(node, bufnr) or ''
|
||||||
|
if #text < 2 or text:sub(1, 1) ~= '"' or text:sub(-1) ~= '"' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local sr, sc, er, ec = node:range()
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc, {
|
||||||
|
end_col = sc + 1,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 210,
|
||||||
|
})
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, er, ec - 1, {
|
||||||
|
end_col = ec,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 210,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hl_raw_string_delims(bufnr, node)
|
||||||
|
local text = vim.treesitter.get_node_text(node, bufnr) or ''
|
||||||
|
if #text < 2 or text:sub(1, 1) ~= '`' or text:sub(-1) ~= '`' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local sr, sc, er, ec = node:range()
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc, {
|
||||||
|
end_col = sc + 1,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 210,
|
||||||
|
})
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, er, ec - 1, {
|
||||||
|
end_col = ec,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 210,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hl_comment_prefix(bufnr, node)
|
||||||
|
local sr, sc, er, ec = node:range()
|
||||||
|
local text = vim.treesitter.get_node_text(node, bufnr) or ''
|
||||||
|
if text:sub(1, 2) == '//' then
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc, {
|
||||||
|
end_col = sc + 2,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 208,
|
||||||
|
})
|
||||||
|
elseif text:sub(1, 2) == '/*' then
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc, {
|
||||||
|
end_col = sc + 2,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 208,
|
||||||
|
})
|
||||||
|
if text:sub(-2) == '*/' then
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, er, ec - 2, {
|
||||||
|
end_col = ec,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 208,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hl_verbs_in_interpreted_string(bufnr, str_node)
|
||||||
|
for idx = 0, str_node:child_count() - 1 do
|
||||||
|
local ch = str_node:child(idx)
|
||||||
|
if ch and ch:type() == 'interpreted_string_literal_content' then
|
||||||
|
local text = vim.treesitter.get_node_text(ch, bufnr) or ''
|
||||||
|
local sr, sc, er, _ = ch:range()
|
||||||
|
if sr == er then
|
||||||
|
for _, span in ipairs(fmt_verb_spans(text)) do
|
||||||
|
extmark_verb(bufnr, sr, sc, text, span[1], span[2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- `nil` is bundled with iota as @constant.builtin; LSP can also tag it as keyword.
|
||||||
|
-- Extmarks beat both so it matches true/false (warm).
|
||||||
|
local function hl_nil_literal(bufnr, node)
|
||||||
|
local sr, sc, er, ec = node:range()
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc, {
|
||||||
|
end_row = er,
|
||||||
|
end_col = ec,
|
||||||
|
hl_group = 'LgwtdNil',
|
||||||
|
priority = 10000,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hl_rune_literal(bufnr, node)
|
||||||
|
local text = vim.treesitter.get_node_text(node, bufnr) or ''
|
||||||
|
if #text < 2 or text:sub(1, 1) ~= "'" or text:sub(-1) ~= "'" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local sr, sc, er, ec = node:range()
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc, {
|
||||||
|
end_col = sc + 1,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 210,
|
||||||
|
})
|
||||||
|
local q0 = ec - 1
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, er, q0, {
|
||||||
|
end_col = ec,
|
||||||
|
hl_group = 'LgwtdQuote',
|
||||||
|
priority = 210,
|
||||||
|
})
|
||||||
|
if ec - sc <= 2 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local inner = text:sub(2, -2)
|
||||||
|
local inner_hl = inner:match('^\\') and 'LgwtdEscape' or 'LgwtdStrInner'
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns, sr, sc + 1, {
|
||||||
|
end_col = ec - 1,
|
||||||
|
hl_group = inner_hl,
|
||||||
|
priority = 190,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.refresh(bufnr)
|
||||||
|
if not vim.api.nvim_buf_is_valid(bufnr) or vim.bo[bufnr].filetype ~= 'go' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
clear_buf(bufnr)
|
||||||
|
local ok, parser = pcall(vim.treesitter.get_parser, bufnr, 'go')
|
||||||
|
if not ok or not parser then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
parser:parse()
|
||||||
|
local trees = parser:trees()
|
||||||
|
local tree = trees and trees[1]
|
||||||
|
if not tree then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local root = tree:root()
|
||||||
|
local q = vim.treesitter.query.parse('go', '(interpreted_string_literal) @s')
|
||||||
|
local nlines = vim.api.nvim_buf_line_count(bufnr)
|
||||||
|
for _, node in q:iter_captures(root, bufnr, 0, nlines) do
|
||||||
|
if node:type() == 'interpreted_string_literal' then
|
||||||
|
hl_verbs_in_interpreted_string(bufnr, node)
|
||||||
|
hl_interpreted_double_quotes(bufnr, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local qraw = vim.treesitter.query.parse('go', '(raw_string_literal) @raw')
|
||||||
|
for _, node in qraw:iter_captures(root, bufnr, 0, nlines) do
|
||||||
|
if node:type() == 'raw_string_literal' then
|
||||||
|
hl_raw_string_delims(bufnr, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local qr = vim.treesitter.query.parse('go', '(rune_literal) @r')
|
||||||
|
for _, node in qr:iter_captures(root, bufnr, 0, nlines) do
|
||||||
|
if node:type() == 'rune_literal' then
|
||||||
|
hl_rune_literal(bufnr, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local qc = vim.treesitter.query.parse('go', '(comment) @c')
|
||||||
|
for _, node in qc:iter_captures(root, bufnr, 0, nlines) do
|
||||||
|
if node:type() == 'comment' then
|
||||||
|
hl_comment_prefix(bufnr, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local qnil = vim.treesitter.query.parse('go', '(nil) @n')
|
||||||
|
for _, node in qnil:iter_captures(root, bufnr, 0, nlines) do
|
||||||
|
if node:type() == 'nil' then
|
||||||
|
hl_nil_literal(bufnr, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local scheduled = {}
|
||||||
|
|
||||||
|
function M.schedule_refresh(bufnr)
|
||||||
|
if scheduled[bufnr] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
scheduled[bufnr] = true
|
||||||
|
vim.schedule(function()
|
||||||
|
scheduled[bufnr] = nil
|
||||||
|
M.refresh(bufnr)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function attach_parser_cbs(bufnr)
|
||||||
|
if vim.b[bufnr].lgwtd_go_ts_cbs then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local ok, parser = pcall(vim.treesitter.get_parser, bufnr, 'go')
|
||||||
|
if not ok or not parser or not parser.register_cbs then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.b[bufnr].lgwtd_go_ts_cbs = true
|
||||||
|
parser:register_cbs({
|
||||||
|
on_changed = function()
|
||||||
|
M.schedule_refresh(bufnr)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local setup_done = false
|
||||||
|
|
||||||
|
function M.setup()
|
||||||
|
if setup_done then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
setup_done = true
|
||||||
|
local aug = vim.api.nvim_create_augroup('LgwtdarkGoExtra', { clear = true })
|
||||||
|
vim.api.nvim_create_autocmd('FileType', {
|
||||||
|
group = aug,
|
||||||
|
pattern = 'go',
|
||||||
|
callback = function(ev)
|
||||||
|
M.attach(ev.buf)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI' }, {
|
||||||
|
group = aug,
|
||||||
|
callback = function(ev)
|
||||||
|
if vim.bo[ev.buf].filetype == 'go' then
|
||||||
|
M.schedule_refresh(ev.buf)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd('ColorScheme', {
|
||||||
|
group = aug,
|
||||||
|
pattern = '*',
|
||||||
|
callback = function()
|
||||||
|
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
||||||
|
if vim.bo[buf].filetype == 'go' then
|
||||||
|
M.schedule_refresh(buf)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.attach(bufnr)
|
||||||
|
if not pcall(vim.treesitter.start, bufnr, 'go') then
|
||||||
|
if vim.g.lgwtd_go_parser_warned == nil then
|
||||||
|
vim.g.lgwtd_go_parser_warned = true
|
||||||
|
vim.notify(
|
||||||
|
'Go Tree-sitter parser is missing. Run :Lazy sync then :TSInstall go',
|
||||||
|
vim.log.levels.WARN,
|
||||||
|
{ title = 'lgwtdark' }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
M.refresh(bufnr)
|
||||||
|
attach_parser_cbs(bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
28
lua/plugins/init.lua
Normal file
28
lua/plugins/init.lua
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-- nvim-treesitter: accurate syntax (your colorscheme maps @highlight groups).
|
||||||
|
-- We pin `master`: the default `main` branch is a rewrite (different API, Nvim 0.12+).
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
'nvim-treesitter/nvim-treesitter',
|
||||||
|
branch = 'master',
|
||||||
|
lazy = false,
|
||||||
|
build = ':TSUpdate',
|
||||||
|
opts = {
|
||||||
|
ensure_installed = {
|
||||||
|
'go',
|
||||||
|
'lua',
|
||||||
|
'vim',
|
||||||
|
'vimdoc',
|
||||||
|
'query',
|
||||||
|
},
|
||||||
|
auto_install = true,
|
||||||
|
highlight = {
|
||||||
|
enable = true,
|
||||||
|
additional_vim_regex_highlighting = false,
|
||||||
|
},
|
||||||
|
indent = { enable = true },
|
||||||
|
},
|
||||||
|
config = function(_, opts)
|
||||||
|
require('nvim-treesitter.configs').setup(opts)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
23
lua/user/cursor.lua
Normal file
23
lua/user/cursor.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- Terminal cursor shapes (Vim t_SI / t_EI / t_SR). Many Neovim builds no longer define these; pcall skips safely.
|
||||||
|
|
||||||
|
local esc = '\27'
|
||||||
|
|
||||||
|
local function set_termcap(name, value)
|
||||||
|
pcall(function()
|
||||||
|
vim.o[name] = value
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
if vim.env.TMUX then
|
||||||
|
set_termcap('t_SI', esc .. 'Ptmux;' .. esc .. esc .. '[6 q' .. esc .. '\\')
|
||||||
|
set_termcap('t_EI', esc .. 'Ptmux;' .. esc .. esc .. '[2 q' .. esc .. '\\')
|
||||||
|
else
|
||||||
|
set_termcap('t_SI', esc .. '[6 q') -- insert: bar
|
||||||
|
set_termcap('t_EI', esc .. '[2 q') -- normal: block
|
||||||
|
end
|
||||||
|
|
||||||
|
set_termcap('t_SR', esc .. '[4 q') -- replace: underline
|
||||||
|
|
||||||
|
-- Modern Neovim uses guicursor for mode shapes (also in many terminals/UIs).
|
||||||
|
-- Keep operator-pending block so d/c don't look like replace mode.
|
||||||
|
vim.opt.guicursor = 'n-v-c-sm:block,i-ci-ve:ver25,r-cr:hor20,o:block'
|
||||||
81
lua/user/keymaps.lua
Normal file
81
lua/user/keymaps.lua
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
-- WASD-style layout (ported from Vim). Uses same keys as your vimrc; 'x' = visual (vnoremap).
|
||||||
|
|
||||||
|
local function n(lhs, rhs, opts)
|
||||||
|
vim.keymap.set('n', lhs, rhs, opts or {})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function x(lhs, rhs)
|
||||||
|
vim.keymap.set('x', lhs, rhs)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function o(lhs, rhs, opts)
|
||||||
|
vim.keymap.set('o', lhs, rhs, opts or {})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Movement (normal, visual, operator-pending)
|
||||||
|
for _, lhs in ipairs({ 'w', 's', 'a', 'd' }) do
|
||||||
|
local rhs = ({ w = 'k', s = 'j', a = 'h', d = 'l' })[lhs]
|
||||||
|
n(lhs, rhs)
|
||||||
|
x(lhs, rhs)
|
||||||
|
o(lhs, rhs)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Word motions (normal + operator; visual keeps native w/W/e/E on i,I,j,J now remapped)
|
||||||
|
for _, mode in ipairs({ 'n', 'o' }) do
|
||||||
|
local opts = mode == 'o' and { nowait = true } or {}
|
||||||
|
vim.keymap.set(mode, 'i', 'w', opts)
|
||||||
|
vim.keymap.set(mode, 'I', 'W', opts)
|
||||||
|
vim.keymap.set(mode, 'j', 'e', opts)
|
||||||
|
vim.keymap.set(mode, 'J', 'E', opts)
|
||||||
|
end
|
||||||
|
-- Operator-pending fixes so combos behave as expected:
|
||||||
|
-- cw -> ci, ce -> cj, and rk does nothing.
|
||||||
|
o('w', 'i', { nowait = true })
|
||||||
|
o('e', 'j', { nowait = true })
|
||||||
|
o('k', '<Nop>', { nowait = true })
|
||||||
|
|
||||||
|
-- Insert / append
|
||||||
|
n('e', 'i')
|
||||||
|
n('E', 'a')
|
||||||
|
n('D', 'A')
|
||||||
|
n('A', 'I')
|
||||||
|
|
||||||
|
-- Operators
|
||||||
|
n('r', 'd', { nowait = true })
|
||||||
|
n('R', 'D')
|
||||||
|
o('r', 'd', { nowait = true })
|
||||||
|
n('c', 'c')
|
||||||
|
o('c', 'c')
|
||||||
|
n('f', 's')
|
||||||
|
n('F', 'S')
|
||||||
|
n('t', 'r')
|
||||||
|
n('T', 'R')
|
||||||
|
|
||||||
|
-- Open lines
|
||||||
|
n('W', 'O')
|
||||||
|
n('S', 'o')
|
||||||
|
|
||||||
|
-- Visual mode entry from normal
|
||||||
|
n('H', 'V')
|
||||||
|
n('h', '<Nop>')
|
||||||
|
|
||||||
|
-- Find / motion
|
||||||
|
n('l', 'f')
|
||||||
|
n('L', 'F')
|
||||||
|
n('p', 't')
|
||||||
|
n('P', 'T')
|
||||||
|
o('l', 'f', { nowait = true })
|
||||||
|
o('L', 'F', { nowait = true })
|
||||||
|
o('p', 't', { nowait = true })
|
||||||
|
o('P', 'T', { nowait = true })
|
||||||
|
|
||||||
|
-- Paste
|
||||||
|
n('v', 'p')
|
||||||
|
n('V', 'P')
|
||||||
|
n('o', 'p')
|
||||||
|
|
||||||
|
n('K', '<Nop>')
|
||||||
|
n('k', '<Nop>')
|
||||||
|
n('O', '<Nop>')
|
||||||
|
x('O', '<Nop>')
|
||||||
|
o('O', '<Nop>')
|
||||||
15
lua/user/options.lua
Normal file
15
lua/user/options.lua
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-- Line numbers
|
||||||
|
vim.opt.number = true
|
||||||
|
vim.opt.relativenumber = true
|
||||||
|
|
||||||
|
-- Indent width4 (spaces); new lines inherit current indent
|
||||||
|
vim.opt.tabstop = 4
|
||||||
|
vim.opt.shiftwidth = 4
|
||||||
|
vim.opt.expandtab = true
|
||||||
|
vim.opt.softtabstop = -1 -- follow shiftwidth (Neovim)
|
||||||
|
|
||||||
|
vim.opt.autoindent = true
|
||||||
|
vim.opt.copyindent = true
|
||||||
|
vim.opt.smartindent = true -- pressing Enter after `{` indents the next line
|
||||||
|
|
||||||
|
vim.cmd('filetype plugin indent on')
|
||||||
4
scratch_go.go
Normal file
4
scratch_go.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package main
|
||||||
|
func main() {
|
||||||
|
t.Errorf("got %q want %q", got, want)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user