docs: lsp, options, api #38980

docs: lsp, options

- revert bogus change to `_meta/builtin_types.lua` from 3a4a66017b

Close #38991

Co-authored-by: David Mejorado <david.mejorado@gmail.com>
This commit is contained in:
Justin M. Keyes
2026-04-14 06:09:54 -04:00
committed by GitHub
parent 65b40e69ac
commit d77808ec59
18 changed files with 173 additions and 125 deletions

View File

@@ -2,10 +2,6 @@ build:
- changed-files:
- any-glob-to-any-file: [ CMakeLists.txt, "**/CMakeLists.txt", "**/Makefile", "**/*.cmake", cmake.deps/**/* ]
checkhealth:
- changed-files:
- any-glob-to-any-file: [ "**/health.lua" ]
ci:
- changed-files:
- any-glob-to-any-file: [ .github/actions/**, .github/workflows/**, .github/scripts/** ]
@@ -38,10 +34,6 @@ documentation:
- changed-files:
- any-glob-to-all-files: [ runtime/doc/*, "**/*.md" ]
editorconfig:
- changed-files:
- any-glob-to-any-file: [ .editorconfig, runtime/lua/editorconfig.lua, runtime/plugin/editorconfig.lua ]
filetype:
- changed-files:
- any-glob-to-any-file: [ runtime/lua/vim/filetype.lua, runtime/lua/vim/filetype/detect.lua ]
@@ -66,6 +58,10 @@ netrw:
- changed-files:
- any-glob-to-any-file: [ runtime/autoload/netrw.vim, runtime/plugin/netrwPlugin.vim ]
plugin-editorconfig:
- changed-files:
- any-glob-to-any-file: [ .editorconfig, runtime/lua/editorconfig.lua, runtime/plugin/editorconfig.lua ]
snippet:
- changed-files:
- any-glob-to-any-file: [ runtime/lua/vim/snippet.lua ]
@@ -89,3 +85,7 @@ treesitter:
tui:
- changed-files:
- any-glob-to-any-file: [ src/nvim/tui/tui.* ]
ux-visibility:
- changed-files:
- any-glob-to-any-file: [ "**/health.lua" ]

View File

@@ -1530,13 +1530,11 @@ nvim_set_current_win({window}) *nvim_set_current_win()*
• {window} (`integer`) |window-ID| to focus
nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
Sets a highlight group.
Sets a highlight group. By default, replaces the entire definition (e.g.
`nvim_set_hl(0, 'Visual', {})` will clear the "Visual" group), unless
`update` is specified.
Note: ~
• Unlike the `:highlight` command which can update a highlight group,
this function completely replaces the definition. For example:
`nvim_set_hl(0, 'Visual', {})` will clear the highlight group
'Visual'.
• The fg and bg keys also accept the string values `"fg"` or `"bg"`
which act as aliases to the corresponding foreground and background
values of the Normal group. If the Normal group has not been defined,
@@ -1573,21 +1571,21 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
• default: boolean Don't override existing definition
|:hi-default|
• dim: boolean
• fg: color name or "#RRGGBB", see note.
• fg: Color name or "#RRGGBB", see note.
• fg_indexed: boolean (default false) If true, fg is a
terminal palette index (0-255).
• font: GUI font name (string). Sets |highlight-font|. Use
"NONE" to clear.
• force: if true force update the highlight group when it
exists.
• force: boolean (default false) Update the highlight group
even if it already exists.
• italic: boolean
• link: Name of highlight group to link to. |:hi-link|
• link_global: Like "link", but always resolved in the global
(ns=0) namespace.
namespace (ns=0).
• nocombine: boolean
• overline: boolean
• reverse: boolean
• sp: color name or "#RRGGBB"
• sp: Color name or "#RRGGBB"
• standout: boolean
• strikethrough: boolean
• undercurl: boolean

View File

@@ -1249,9 +1249,9 @@ items:
completion kind
match See "matches" in |complete_info()|.
preselect when non-zero this item is selected by default in the
popup menu. Only effective when 'completeopt' contains
"preselect". If multiple items have "preselect" set,
the first one is used.
popup menu. Only if 'completeopt' has "preselect". If
multiple items have "preselect" set, the first one is
used.
All of these except "icase", "equal", "dup" and "empty" must be a string. If
an item does not meet these requirements then an error message is given and

View File

@@ -357,13 +357,16 @@ FAQ *lsp-faq*
================================================================================
LSP API *lsp-api*
The |lsp-core| API provides core functions for creating and managing clients.
The |lsp-buf| functions perform operations for LSP clients attached to the
current buffer.
The |lsp-core| API provides core functions for creating and managing clients,
You can even create creating custom (in-process) |lsp-server|s.
The |lsp-buf| functions operate LSP clients attached to the current buffer.
------------------------------------------------------------------------------
CONCEPTS
*lsp-method*
Requests and notifications defined by the LSP specification are referred to as
"LSP methods". These are handled by Lua |lsp-handler| functions.
"LSP methods". These are handled by |lsp-handler| functions.
*lsp-handler*
LSP handlers are functions that handle |lsp-response|s to requests made by Nvim
@@ -453,7 +456,8 @@ Handlers can be set by (in increasing priority):
Log levels are defined in |vim.log.levels|
VIM.LSP.PROTOCOL *vim.lsp.protocol*
------------------------------------------------------------------------------
PROTOCOL *vim.lsp.protocol*
Module `vim.lsp.protocol` defines constants dictated by the LSP specification,
and helper functions for creating protocol-related objects.
@@ -485,6 +489,71 @@ LSP notification shape: >
<
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage
------------------------------------------------------------------------------
IN-PROCESS LSP SERVER *lsp-server*
For maximum flexibility you may want to create your own server. This allows
you to hook into Nvim LSP features automatically. The "server" can be an
in-process Lua function instead of an external process. For example see
`runtime/lua/vim/pack/_lsp.lua` which provides features like "docs
hover" in the vim.pack "Confirm Updates" UI.
This is possible because |vim.lsp.start()| accepts `cmd` as a Lua function.
The function must accept `vim.lsp.rpc.Dispatchers` and return a table in the
form of |vim.lsp.rpc.Client|. Use `dispatchers.on_exit()` to signal that the
server has stopped.
Example (select and run the code with `:lua`, then check `:che vim.lsp` and
look for "my-server"): >lua
--- @return vim.lsp.rpc.Client
local function cmd_fn(dispatchers)
local closing = false
local request_id = 0
local srv = {} --[[@type vim.lsp.rpc.Client]]
function srv.request(method, params, callback)
if method == 'initialize' then
callback(nil, {
capabilities = {
hoverProvider = true,
},
})
elseif method == 'shutdown' then
callback(nil, nil)
end
request_id = request_id + 1
return true, request_id
end
function srv.notify(method, params)
if method == 'exit' then
dispatchers.on_exit(0, 15)
end
end
function srv.is_closing()
return closing
end
function srv.terminate()
closing = true
end
return srv
end
-- Define a config for the server, then enable it...
vim.lsp.config['my-server'] = {
cmd = cmd_fn,
filetypes = { 'lua' },
root_markers = { '.git' },
}
vim.lsp.enable('my-server')
-- ...or call start() directly.
vim.lsp.start(
{ cmd = cmd_fn, name = 'my-server', root_dir = vim.uv.cwd() },
{ attach = true })
<
================================================================================
LSP HIGHLIGHT *lsp-highlight*
@@ -1252,7 +1321,7 @@ start({config}, {opts}) *vim.lsp.start()*
are:
• `name` arbitrary name for the LSP client. Should be unique per language
server.
• `cmd` command string[] or function.
• `cmd` command string[] or function. See also |lsp-server|.
• `root_dir` path to the project root. By default this is used to decide
if an existing client should be re-used. The example above uses
|vim.fs.root()| to detect the root by traversing the file system upwards
@@ -1749,14 +1818,14 @@ Lua module: vim.lsp.client *lsp-client*
server (treated as in |jobstart()|, must be
absolute or on `$PATH`, shell constructs like
"~" are not expanded), or function that creates
an RPC client. Function receives a
`dispatchers` table and the resolved `config`,
and must return a table with member functions
`request`, `notify`, `is_closing` and
`terminate`. See |vim.lsp.rpc.request()|,
|vim.lsp.rpc.notify()|. For TCP there is a
builtin RPC client factory:
|vim.lsp.rpc.connect()|
an RPC client (or an in-process |lsp-server|).
Function receives a `dispatchers` table and the
resolved `config`, and must return an object in
the form of |vim.lsp.rpc.Client|.
See |vim.lsp.rpc.request()|
|vim.lsp.rpc.notify()|
• For TCP there is a builtin RPC client
factory: |vim.lsp.rpc.connect()|
• {cmd_cwd}? (`string`, default: cwd) Directory to launch
the `cmd` process. Not related to `root_dir`.
• {cmd_env}? (`table`) Environment variables passed to the

View File

@@ -896,8 +896,9 @@ Events: ~
Example: >lua
local progress = {
kind = 'progress',
status = 'running',
percent = 10,
source = 'my-plugin',
status = 'running',
title = 'term',
}
progress.id = vim.api.nvim_echo({ { 'searching...' } }, true, progress)

View File

@@ -32,7 +32,6 @@ LSP
LUA
• vim.pos, vim.range always require the `buf` parameter.
DIAGNOSTICS
@@ -106,8 +105,6 @@ The following new features were added.
API
• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.definition()|,
and |vim.lsp.buf.implementation()| now follows 'switchbuf'.
• |nvim_set_hl()| supports "font" key.
BUILD
@@ -137,15 +134,11 @@ HIGHLIGHTS
LSP
• LSP capabilities:
`textDocument/foo` …
https://microsoft.github.io/language-server-protocol/specification/#textDocument_foo
• `textDocument/bar` …
https://microsoft.github.io/language-server-protocol/specification/#textDocument_bar
Completion supports `CompletionItem.preselect` if 'completeopt' has
"preselect". https://microsoft.github.io/language-server-protocol/specification/#completionClientCapabilities
• |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.definition()|,
and |vim.lsp.buf.implementation()| now follows 'switchbuf'.
• Support for nested snippets.
• LSP completion now supports `CompletionItem.preselect`. Requires
'completeopt' to include "preselect".
LUA

View File

@@ -1665,8 +1665,7 @@ A jump table for the options with a short description can be found at |Q_op|.
"menu" or "menuone". No effect if "longest" is present.
noselect Same as "noinsert", except that no menu item is
pre-selected. If both "noinsert" and "noselect" are
present, "noselect" takes precedence. This is enabled
pre-selected. Takes precedence over "noinsert". Enabled
automatically when 'autocomplete' is on, unless
"preinsert" is also enabled.
@@ -1688,14 +1687,13 @@ A jump table for the options with a short description can be found at |Q_op|.
'ignorecase' is set without 'infercase'.
See also |preinserted()|.
preselect Selects the first completion item whose "preselect"
field is set, if any. Takes precedence over "noselect".
preview Show extra information about the currently selected
completion in the preview window. Only works in
combination with "menu" or "menuone".
preselect Select the completion item that has the "preselect"
attribute set. If both "noselect" and "preselect" are present,
"preselect" takes precedence.
Only "fuzzy", "longest", "popup", "preinsert", "preselect" and
"preview" have an effect when 'autocomplete' is enabled.
@@ -3206,15 +3204,15 @@ A jump table for the options with a short description can be found at |Q_op|.
'formatprg' 'fp' string (default "")
global or local to buffer |global-local|
Disallowed in |modeline|. |no-modeline-option|
The name of an external program that will be used to format the lines
selected with the |gq| operator. The program must take the input on
stdin and produce the output on stdout. The Unix program "fmt" is
such a program.
If the 'formatexpr' option is not empty it will be used instead.
Otherwise, if 'formatprg' option is an empty string, the internal
format function will be used |C-indenting|.
Environment variables are expanded |:set_env|. See |option-backslash|
about including spaces and backslashes.
External program used to format lines with |gq|. Ignored if
'formatexpr' is set.
If empty, the internal |C-indenting| function will be used.
The program must take input on stdin and produce output on stdout. The
Unix program "fmt" is such a program. Environment variables are
expanded |:set_env|. See |option-backslash| about including spaces
and backslashes.
*'fsync'* *'fs'* *'nofsync'* *'nofs'*
'fsync' 'fs' boolean (default on)

View File

@@ -2204,14 +2204,10 @@ function vim.api.nvim_set_current_win(window) end
--- ```
function vim.api.nvim_set_decoration_provider(ns_id, opts) end
--- Sets a highlight group.
--- Sets a highlight group. By default, replaces the entire definition (e.g. `nvim_set_hl(0, 'Visual', {})`
--- will clear the "Visual" group), unless `update` is specified.
---
--- Note:
--- Unlike the `:highlight` command which can update a highlight group,
--- this function completely replaces the definition. For example:
--- `nvim_set_hl(0, 'Visual', {})` will clear the highlight group
--- 'Visual'.
---
--- The fg and bg keys also accept the string values `"fg"` or `"bg"`
--- which act as aliases to the corresponding foreground and background
--- values of the Normal group. If the Normal group has not been defined,
@@ -2240,17 +2236,17 @@ function vim.api.nvim_set_decoration_provider(ns_id, opts) end
--- - ctermfg: Sets foreground of cterm color `ctermfg`
--- - default: boolean Don't override existing definition `:hi-default`
--- - dim: boolean
--- - fg: color name or "#RRGGBB", see note.
--- - fg: Color name or "#RRGGBB", see note.
--- - fg_indexed: boolean (default false) If true, fg is a terminal palette index (0-255).
--- - font: GUI font name (string). Sets `highlight-font`. Use "NONE" to clear.
--- - force: if true force update the highlight group when it exists.
--- - force: boolean (default false) Update the highlight group even if it already exists.
--- - italic: boolean
--- - link: Name of highlight group to link to. `:hi-link`
--- - link_global: Like "link", but always resolved in the global (ns=0) namespace.
--- - link_global: Like "link", but always resolved in the global namespace (ns=0).
--- - nocombine: boolean
--- - overline: boolean
--- - reverse: boolean
--- - sp: color name or "#RRGGBB"
--- - sp: Color name or "#RRGGBB"
--- - standout: boolean
--- - strikethrough: boolean
--- - undercurl: boolean

View File

@@ -201,7 +201,7 @@ error('Cannot require a meta file')
--- @field priority? integer
--- @class vim.fn.sign_placelist.list.item
--- @field buf integer|string
--- @field buffer integer|string
--- @field group? string
--- @field id? integer
--- @field lnum? integer|string
@@ -209,11 +209,11 @@ error('Cannot require a meta file')
--- @field priority? integer
--- @class vim.fn.sign_unplace.dict
--- @field buf? integer|string
--- @field buffer? integer|string
--- @field id? integer
--- @class vim.fn.sign_unplacelist.list.item
--- @field buf? integer|string
--- @field buffer? integer|string
--- @field group? string
--- @field id? integer

View File

@@ -1181,8 +1181,7 @@ vim.go.cia = vim.go.completeitemalign
--- "menu" or "menuone". No effect if "longest" is present.
---
--- noselect Same as "noinsert", except that no menu item is
--- pre-selected. If both "noinsert" and "noselect" are
--- present, "noselect" takes precedence. This is enabled
--- pre-selected. Takes precedence over "noinsert". Enabled
--- automatically when 'autocomplete' is on, unless
--- "preinsert" is also enabled.
---
@@ -1204,14 +1203,13 @@ vim.go.cia = vim.go.completeitemalign
--- 'ignorecase' is set without 'infercase'.
--- See also `preinserted()`.
---
--- preselect Selects the first completion item whose "preselect"
--- field is set, if any. Takes precedence over "noselect".
---
--- preview Show extra information about the currently selected
--- completion in the preview window. Only works in
--- combination with "menu" or "menuone".
---
--- preselect Select the completion item that has the "preselect"
--- attribute set. If both "noselect" and "preselect" are present,
--- "preselect" takes precedence.
---
--- Only "fuzzy", "longest", "popup", "preinsert", "preselect" and
--- "preview" have an effect when 'autocomplete' is enabled.
---
@@ -2960,15 +2958,15 @@ vim.o.fo = vim.o.formatoptions
vim.bo.formatoptions = vim.o.formatoptions
vim.bo.fo = vim.bo.formatoptions
--- The name of an external program that will be used to format the lines
--- selected with the `gq` operator. The program must take the input on
--- stdin and produce the output on stdout. The Unix program "fmt" is
--- such a program.
--- If the 'formatexpr' option is not empty it will be used instead.
--- Otherwise, if 'formatprg' option is an empty string, the internal
--- format function will be used `C-indenting`.
--- Environment variables are expanded `:set_env`. See `option-backslash`
--- about including spaces and backslashes.
--- External program used to format lines with `gq`. Ignored if
--- 'formatexpr' is set.
---
--- If empty, the internal `C-indenting` function will be used.
---
--- The program must take input on stdin and produce output on stdout. The
--- Unix program "fmt" is such a program. Environment variables are
--- expanded `:set_env`. See `option-backslash` about including spaces
--- and backslashes.
---
--- @type string
vim.o.formatprg = ""

View File

@@ -702,7 +702,7 @@ end
--- See |vim.lsp.ClientConfig| for all available options. The most important are:
---
--- - `name` arbitrary name for the LSP client. Should be unique per language server.
--- - `cmd` command string[] or function.
--- - `cmd` command string[] or function. See also |lsp-server|.
--- - `root_dir` path to the project root. By default this is used to decide if an existing client
--- should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing
--- the file system upwards starting from the current directory until either a `pyproject.toml`

View File

@@ -165,9 +165,12 @@ function M.init(client, bufnr)
end
end
--- Sends didOpen/didClose/didSave to all client groups.
---
--- @param bufnr integer
function M._send_did_save(bufnr)
local groups = {} ---@type table<string,vim.lsp.CTGroup>
-- Collect all client groups.
for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do
local group = get_group(client)
groups[group_key(group)] = group
@@ -176,6 +179,7 @@ function M._send_did_save(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local text = vim.func._memoize('concat', vim.lsp._buf_get_full_text)
-- Send didOpen/didClose/didSave to all client groups.
for _, group in pairs(groups) do
local name = api.nvim_buf_get_name(bufnr)
local state = state_by_group[group]

View File

@@ -51,10 +51,10 @@ local all_clients = {}
---
--- Command `string[]` that launches the language server (treated as in |jobstart()|, must be
--- absolute or on `$PATH`, shell constructs like "~" are not expanded), or function that creates an
--- RPC client. Function receives a `dispatchers` table and the resolved `config`, and must return
--- a table with member functions `request`, `notify`, `is_closing` and `terminate`.
--- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|.
--- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
--- RPC client (or an in-process |lsp-server|). Function receives a `dispatchers` table and the
--- resolved `config`, and must return an object in the form of |vim.lsp.rpc.Client|.
--- - See |vim.lsp.rpc.request()| |vim.lsp.rpc.notify()|
--- - For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()|
--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers, config: vim.lsp.ClientConfig): vim.lsp.rpc.Client
---
--- Directory to launch the `cmd` process. Not related to `root_dir`.

View File

@@ -1050,11 +1050,7 @@ function M.show_document(location, position_encoding, opts)
if vim.api.nvim_get_mode().mode == 'i' then
local line = api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] or ''
if col >= #line then
vim.api.nvim_feedkeys(
vim.api.nvim_replace_termcodes('<End>', true, false, true),
'n',
false
)
vim.api.nvim_feedkeys(vim.keycode('<End>'), 'n', false)
end
end
end

View File

@@ -120,12 +120,8 @@ DictAs(get_hl_info) nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena
return ns_get_hl_defs((NS)ns_id, opts, arena, err);
}
/// Sets a highlight group.
///
/// @note Unlike the `:highlight` command which can update a highlight group,
/// this function completely replaces the definition. For example:
/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group
/// 'Visual'.
/// Sets a highlight group. By default, replaces the entire definition (e.g. `nvim_set_hl(0, 'Visual', {})`
/// will clear the "Visual" group), unless `update` is specified.
///
/// @note The fg and bg keys also accept the string values `"fg"` or `"bg"`
/// which act as aliases to the corresponding foreground and background
@@ -154,17 +150,17 @@ DictAs(get_hl_info) nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena
/// - ctermfg: Sets foreground of cterm color |ctermfg|
/// - default: boolean Don't override existing definition |:hi-default|
/// - dim: boolean
/// - fg: color name or "#RRGGBB", see note.
/// - fg: Color name or "#RRGGBB", see note.
/// - fg_indexed: boolean (default false) If true, fg is a terminal palette index (0-255).
/// - font: GUI font name (string). Sets |highlight-font|. Use "NONE" to clear.
/// - force: if true force update the highlight group when it exists.
/// - force: boolean (default false) Update the highlight group even if it already exists.
/// - italic: boolean
/// - link: Name of highlight group to link to. |:hi-link|
/// - link_global: Like "link", but always resolved in the global (ns=0) namespace.
/// - link_global: Like "link", but always resolved in the global namespace (ns=0).
/// - nocombine: boolean
/// - overline: boolean
/// - reverse: boolean
/// - sp: color name or "#RRGGBB"
/// - sp: Color name or "#RRGGBB"
/// - standout: boolean
/// - strikethrough: boolean
/// - undercurl: boolean

View File

@@ -1652,8 +1652,7 @@ local options = {
"menu" or "menuone". No effect if "longest" is present.
noselect Same as "noinsert", except that no menu item is
pre-selected. If both "noinsert" and "noselect" are
present, "noselect" takes precedence. This is enabled
pre-selected. Takes precedence over "noinsert". Enabled
automatically when 'autocomplete' is on, unless
"preinsert" is also enabled.
@@ -1675,14 +1674,13 @@ local options = {
'ignorecase' is set without 'infercase'.
See also |preinserted()|.
preselect Selects the first completion item whose "preselect"
field is set, if any. Takes precedence over "noselect".
preview Show extra information about the currently selected
completion in the preview window. Only works in
combination with "menu" or "menuone".
preselect Select the completion item that has the "preselect"
attribute set. If both "noselect" and "preselect" are present,
"preselect" takes precedence.
Only "fuzzy", "longest", "popup", "preinsert", "preselect" and
"preview" have an effect when 'autocomplete' is enabled.
@@ -3763,15 +3761,15 @@ local options = {
abbreviation = 'fp',
defaults = '',
desc = [=[
The name of an external program that will be used to format the lines
selected with the |gq| operator. The program must take the input on
stdin and produce the output on stdout. The Unix program "fmt" is
such a program.
If the 'formatexpr' option is not empty it will be used instead.
Otherwise, if 'formatprg' option is an empty string, the internal
format function will be used |C-indenting|.
Environment variables are expanded |:set_env|. See |option-backslash|
about including spaces and backslashes.
External program used to format lines with |gq|. Ignored if
'formatexpr' is set.
If empty, the internal |C-indenting| function will be used.
The program must take input on stdin and produce output on stdout. The
Unix program "fmt" is such a program. Environment variables are
expanded |:set_env|. See |option-backslash| about including spaces
and backslashes.
]=],
expand = true,
full_name = 'formatprg',

View File

@@ -266,8 +266,9 @@ describe('completion', function()
feed('i<C-r>=TestComplete()<CR><ESC>')
eq(0, eval('&l:modified'))
end)
describe('"preselect"', function()
it('"preselect" selects first item with preselect attribute', function()
it('selects first item.preselect item', function()
source([[
function! TestComplete() abort
call complete(1, [

View File

@@ -1548,7 +1548,7 @@ describe('vim.lsp.completion: integration', function()
eq('hallo', n.api.nvim_get_current_line())
end)
it('preselect completion item', function()
it('CompletionItem.preselect', function()
local completion_list = {
isIncomplete = false,
items = {