| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Work towards enabling ["flow" layout](https://github.com/neovim/neovim/blob/9244da12e400ec0291450a5e9400d995e7a29b83/src/gen/gen_help_html.lua#L86-L88)
in all `:help` files.
Problem:
The final boss of vimdoc pseudo-structure: Vim's quasi-headings on
function signatures and excmd docs is visually distinctive but too
inconsistent for a nice parse rule.
Solution:
- Define the quasi-heading format as an "h4" variant, specified at
`:help help-tag-heading`.
- For simplicity and predictability, it is defined as any line that
ends with a help tag(s) preceded by 2 or more spaces (or 1 tab).
- Update existing docs.
- This one-time update has a lot of changes, but is very unlikely to
conflict with ported Vim-patches because (1) Vim rarely adds new
*excmds* that we want, and (2) *function* docs are auto-generated.
ref https://github.com/neovim/tree-sitter-vimdoc/issues/110
|
| |
|
| |
highlights.scm: `#match` is slow, prefer `#lua-match`.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem:
After clearing cursors, "]C" "[C" do nothing, although the previous
cursor positions ("gQ") may still be useful as marks.
Solution:
If multicursor is not active, "]C" "[C" iterate the
"nvim.multicursor.last" extmarks. They also work as an operator motion
("d]C"), since there is no cascade.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
- `v:cascading` is false during the Insert-mode live-preview.
- `vim.on_key()` sees the internal keys of the Visual-selection preview
and "zq" steps.
Solution:
- Set `v:cascading` during the Insert-mode preview.
- Don't send internal keys to `on_key` callbacks.
- Use `v:cascading` as the source of truth for `mc_replaying()`.
|
| |
|
|
|
| |
Problem: No way to check if cascade is in action.
Solution: Add `v:cascading` variable.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
With multicursors, an Insert-mode edit by a Lua/`<Cmd>` mapping or API
(nvim_put(), …) is live-previewed at the other cursors, but dropped at
ESC: the cascade replays _redo_, which does not contain such edits.
Solution:
When `<Cmd>`/Lua/RPC changes the buffer during Insert-mode, sync the
previews and restart the capture (like a non-captured cursor-move). The
edit stays as text.
- The mapping runs only at the primary. Its text is mirrored as-is
(similar to insert-completion). Re-running it per cursor would require
a "partial-remap" span replay, and breaks single-session plugins
(vim.snippet).
- Not during completion; redo already captures the pre-inserted
candidate on accept.
- Text typed before the edit is kept as mirrored, not replayed, so
per-cursor effects ('textwidth' wrapping, …) don't apply to it.
Known issues:
- "o"/"O"/"cc" with 'autoindent' followed by the mapping: the per-cursor
insert-entry replay is still pending, so the edit is lost.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- At session end, `'< '>` revert to the session-start snapshot, which
drops a selection made during the session. And the snapshot may be
from another buffer.
- `zqgj` skips screen rows: `zq` tries the motion before the steps, and
those trial runs advance `w_curswant`.
Solution:
- Keep the primary's `'< '>` at session-end.
- Restore `w_curswant` after the trial runs and after each run of steps.
|
| |
|
|
|
|
|
|
|
|
|
| |
- `{Visual}Q` / `{Visual}zqj`: for linewise motion ("j"), every line
gets a cursor.
- `zq`: handle 3-key motions (`g'a`), don't scroll the view, preserve
the search direction, update existing cursors `gv` target.
- Disallow `zq` in macros.
- Abort `zq` on non-motion ("zqx").
- Classify `g'x` classify like `'x`.
- Classify `'(` `'{` etc. as `type=motion` (they are cursor-relative).
- Perf: skip save/restore of marks when cascading motions ('follow').
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`nvim_mcursor` is not fully baked, and not strongly motivated,
currently. The main programmatic interface for cursors is the
"nvim.multicursor" extmarks namespace.
Solution:
Drop `nvim_mcursor`. Add an internal hook so that multicursor can react
when extmarks are added to the "nvim.multicursor" namespace.
TODO:
If extmarks/namespaces are clumsy to work with, we should _fix that_
rather than adding wrappers.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- `[count]Q` and `{Visual}Q` are implemented as ad hoc utils, because
`Q` is not an "operator".
- `Q` always "toggles", it is not "idempotent".
- Various *cursor-relative* marks (`'.`, `'[`) are not saved/restored
per-cursor, so they don't work during cascade.
- Insert cascade clobbers the primary's `'^` and changelist.
Solution:
- Introduce the `zq{motion}` operator, which places cursors at every
reachable `{motion}`.
- `{Visual}Q` is now an alias to `{Visual}zqj`.
- Introduce `1Q` and `2Q` for idempotent cursor placement.
- Drop `[count]Q`, it's redundant with `zqgn`.
- Classify cursor-relative marks as `type=motion` instead of
`type=jump`. Save/restore per-cursor during cascade.
|
| |
|
|
|
|
|
|
| |
Problem:
- gQ does not restore follow-mode (q=).
- Users/plugins have no way to check if follow-mode is enabled.
Solution:
Introduce buffer-local 'follow' option.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Unlike "gv", "gQ" does not restore the primary-cursor position. This is
inconvenient for workflows where you temporarily clear the cursors,
navigate somewhere, then want to return to the multicursor session.
Solution:
Store the primary-cursor position at extmark-id 1 of the
`'nvim.multicursor.last'` namespace.
Note: users that *don't* want to restore the primary-cursor position can
delete extmark-id 1 from the `'nvim.multicursor.last'` namespace just
before `gQ`. (Intentionally undocumented because this interface is not
final, and should not block Nvim 0.13 release.)
vim.keymap.set('n', 'gQ', function()
local ns = vim.api.nvim_create_namespace('nvim.multicursor.last')
pcall(vim.api.nvim_buf_del_extmark, 0, ns, 1)
return 'gQ'
end, { expr = true })
|
| |
|
|
|
|
|
| |
Problem:
gQ is nonsense during an active multicursor session.
Solution:
Make it a no-op.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- A failed motion in a replay (`vt;` where there is no `;`) flushes the
rest of the replay, so `vt;cX` inserts `X` without changing the
selection.
- If the Insert-entering cmd fails at a cursor (`ct;`, `cgn` with no
match), the text is still inserted at the failed cursor.
Solution:
- `beep_flush()`: Don't flush typeahead (i.e. keys following the failed
cmd) during mc-replay.
- Aligns with interactively-typed behavior.
- Remove a cursor if its Insert-entering command aborts
(`beep && !changedtick`).
- This is particular to Insert-entering commands (`c{motion}`),
because they are tricky. We don't usually remove cursors if
a replayed cmd happens to fail at some cursors.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Incoherent classification of motions vs jumps.
Solution:
Define "motion" as cursor-relative, and "jump" as "absolute".
- Classify G/gg/H/L/M/go/N% as `CmdAtom.type=jump`.
- Classify `*`/`#` as motions.
Behavior changes:
- `*` in follow-mode now moves each cursor to the next occurrence of its
own word.
- Note: the "last search pattern" is not stored per-cursor, so `n`
still searches for the primary's search pattern.
- G/gg/H/M/L in follow-mode do not cascade (would converge/dedupe all cursors).
- If you want to force cursors to converge for some reason, use `9999j`.
- Also affects mappings that mix an absolute move with an edit. E.g.
mapping `nnoremap X ggdd` only replays `dd` per-cursor now. This is
how marks already behaved.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Jumping to a cursor adds one at the old position, so navigation has a
side-effect. "]CQ" no longer deletes a cursor, and returning with "[C"
after wandering off adds a cursor wherever you were.
This is useful in some cases, but as a default it is surprising. The
default behavior should be simple and composable.
Solution:
Revert the "]C"/"[C" cursor-plopping behavior.
Document a mapping for the "rotate" behavior.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`[count]Q` cannot be limited to a Visual selection.
Solution:
`{Visual}[count]Q` places cursors on matches within a Visual selection
(linewise).
TODO:
This works linewise, even if the visual selection is charwise/block. If
we really want to limit by charwise/block selection, then we should add
that feature to `seachpos()` or similar. Every caller of `seachpos()`
should not have to cobble together bespoke support thereof.
|
| |
|
|
|
|
|
|
|
| |
Problem:
`{Visual}Q` moves the primary to the top of the selection, for no
particular reason. And it's inconsistent with `[count]Q`.
Solution:
Keep the primary on the line where the selection ended, aligned to the
shared screen column so it still coincides with that line's cursor.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
In follow-mode, a motion that fails at the primary cursor (e.g. "j" at
EOB) still cascades to other cursors, so they move while the primary
does not, which causes them to be deduplicated...
Also affects Visual sessions: "Vjd" at EOB deletes 1 line at the primary
but (potentially) 2 lines at every multicursor.
Solution:
If a motion "beeps", don't replay it.
"3w" near the end still replays (it moved), as does "0" at column
0 (does not beep). This feels like a usable and intuitive compromise.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- With `clipboard=unnamed[plus]`, per-cursor yanks are joined into '"'
on exit, but not the clipboard.
- Multicursor `"+yy` calls the clipboard provider per-cursor, behaving
as "last wins".
Solution:
- Never cascade to/from the clipboard provider.
- Implicit clipboard (`clipboard=unnamed[plus]`) uses the cursor-local
unnamed register, so the cascade needs no `start_batch_changes()`. On
exit, the clipboard gets the same joined result as the `"` register.
- Operations that reference the explicit clipboard registers `"+` / `"*`
while multicursor is active, use the cached (primary) clipboard value.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Cursors placed same-line before the primary, or producing multiline
edits on lines above the primary, shift the text in a way that breaks
the insert-mode live-mirroring.
Solution:
Skip live-mirroring under those conditions.
TODO (future): we could support live-mirroring if ins-completion's
internal bookkeeping tracked its regions more precisely.
|
| |
|
|
|
|
|
|
|
| |
Problem:
`]C` jumps the primary onto another cursor, which dedupes at the next
edit. Every `]C` consumes a cursor.
Solution:
`]C` adds a cursor at the current position before jumping. This
effectively "rotates" the primary cursor.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
An atom queued in buffer A cascades on B's cursors if the mapping ends
in B ("nnoremap X x:bnext<CR>").
Solution:
Check the atom's origin buffer (`CmdAtom.origin.buf`).
Note: This does not preclude mappings etc from doing work in temporary
throwaway buffers, as long as they return to the origin buffer.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
If an edit cascades to cursor(s) that happen to be within a closed fold,
the edit acts on the fold itself (`:h fold-behavior`). E.g. "dd" deletes
the entire fold, not the cursor's line within the fold.
Solution:
Check `mc_replaying()` in `hasAnyFolding()`, so folds are ignored during
a multicursor cascade. This means multicursor always replays relative to
text *within* the fold.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Other (squashed) commits:
fix(tui): emit ui_send output atomically with the frame
Problem:
tui_ui_send() writes directly to the TTY, bypassing the output buffer.
Sequences sent via nvim_ui_send() (e.g. kitty multiple-cursors, or
visual-dot-repeat) always arrive in a separate TTY write from the frame
they were computed for. This manifests as "tearing", or e.g. in the case
of multicursor the terminal renders text with stale cursor overlays.
Solution:
- tui_ui_send(): while a frame is being assembled (pending invalid
regions or buffered output), buffer instead of writing directly.
- Out-of-frame sends (tty queries, clear-on-disable) still write
immediately.
- mcursor.lua: emit the terminal-cursor update at the end of the redraw
cycle (`on_end`, when screen positions are final) instead of
vim.schedule().
|
| |
|
|
|
|
|
|
| |
Problem:
The "." example mapping at `:h edit-repeat` doesn't work well with
`nvim_feedkeys(…, 'mt', false)`.
Solution:
Use `vim.b[ev.buf].maxseq` instead of `undotree()`.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Undo places the cursor wherever the cursor happened to sit at "save
time" (`uh_cursor` is sampled lazily on the first change).
Examples:
- `i` preserves, but `a` does not
- `diw`, `atest<Esc>`, `d^` abandon the original position
- `D`, `o` restore it (by accident).
Solution:
`composite` tracks the pending atom (and its `origin`) across frames.
A `stuffed` continuation frame inherits the `origin` + prepped redo.
Store `origin` info in the undo header, so undo can restore it.
- Not for a mid-command undo break (i_CTRL-G_u).
- Undoing a mapping restores where the mapping started (which
technically may be different than where the "edit" started).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
The "!" operator stuffs its cmdline continuation (`:.,.+1!`), so its
frame ends before capture (stuff pending) and the redo-prep disappears
with it.
!ipsort<CR> => { type='excmd', lhs=':.,.+1!sort<NL>', keys=':.,.+1!sort<NL>' }
Compare to builtin "." which works bc `do_bang()` completes the redo
(`!ip` + `sort<NL>`).
Solution:
Appoint the stuffed continuation frame as the "redo-prep" frame.
!ipsort<CR> => { type='operator', operator='!', lhs='!ipsort<NL>', keys='!ipsort<NL>' }
Notes:
- atom_cmd_end(): a frame ending with stuff pending re-points its
redo-prep to the next frame.
- atom_cmd_start(): a stuffed continuation frame (`KeyStuffed`) keeps
the redo-prep; flushed stuff discards it.
|
| |
|
|
|
| |
Co-authored-by: darkdi <rantovov5@gmail.com>
Co-authored-by: Qiaoxi Guo <28090444+breadtitor@users.noreply.github.com>
Co-authored-by: zaveshaa <zaveshaa@gmail.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A mapping that ends mid-operation (`nnoremap ,D d`) emits a content-free
"mapping" atom plus a `pending` field, and the "continuation" motion
arrives as a sibling atom. Consumers must stitch the two together (which
has broken cases, e.g. Insert-opening mappings (",i") lose their session
entirely).
- ",i": the session atom is dropped bc the mapping RHS is consumed
before the session starts (typebuf_maplen()==0), so
atom_is_user_input()=false.
- ":normal"-in-opfunc: the opfunc internal "v..y" session (a) became
kVatomTyped just because the deferred composite was open, masking the
real operator capture via atom_captures, and (b) its nested frames
re-derived the outer redo.
- "Motion" based on `moved=true`, has false negatives.
- `CmdAtom.remap` is unnecessary, and clutters the docs/usage.
Solution:
- Introduce `frame_id` to identify CmdFrames.
- Classify `type=motion` better, via `NV_MOTION` flag on the `nv_cmds` table.
- Drop `CmdAtom.pending`, `CmdAtom.remap`.
- Defer atom_composite_end() at the clock edge while an operator is
pending, Visual is active, or `restart_edit` is set: the composite
keeps collecting, so the continuation is captured in the mapping atom.
- ",i": Now an open composite counts as user input.
- ":normal"-in-opfunc: Now handled correctly.
- `remap` is now decided by `composite.payload || 0 subatoms`.
atom_payload_mark() records the read the resolution never captures.
- `toplevel` is now decided by `CmdFrame.parent == NULL`.
before/after:
INPUT BEFORE AFTER
---------------------------------------------------------------------
,D w {mapping lhs=,D pending=operator} {operator lhs=,Dw keys=dw}
+ {operator keys=dw}
ysiw" {mapping lhs=ys pending=operator} {operator lhs=ysiw" keys=g@iw"}
+ {operator lhs=g@iw"}
,v d {mapping pending=visual} {visual lhs=,vd keys=viwd}
+ {visual lhs=viwd}
,i XY<Esc> {normal keys=i lhs=,iXY<Esc>} {insert keys=1iXY<Esc> text=XY}
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- `lhs` is not fully realized. E.g. for a "payload" mapping
`lhs` omits the `getchar()` payload during a mapping (vim-surround
`ds'` reports `lhs="ds"`). This means plugins like vim-repeat are
still needed...
- The "delta" fields of a CmdAtom are calculated too late.
- `<abuf>` and `changed` check whatever (wrong) buffer a command
(":bnext") might land in.
- CTRL-W_w between two windows on the same buffer reports type="motion".
Solution:
- `CmdOrigin` samples (buf/win/cursor/changedtick) at each "scope" entry
(CmdFrame, composite, Visual session, insert session).
- `dd<C-w>l` reports `changed=true` for the buffer it edited, regardless
of where the cursor ends up.
- New fields:
- `pos`: cursor position at command start.
- `moved`: indicates whether the cursor moved (in same buffer).
- `undoseq`: undo state at settlement.
- lhs now includes the payload: "ds)" reports lhs="ds)" instead of "ds".
- Easy for users to "replay" any atom.
- Rename: type "command" => "normal", "ex" => "excmd"; `arg` => `cmdarg`
- Drop `cascade` field (no reason to expose it)
|
| |
|
| |
Extract vimscript.txt from repeat.txt
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
There is no unified notion of a "user action".
Vim processes input by one-char-at-a-time, and mostly throws away any
hints it might gather about the user's action, with one exception: it
stores the last _edit_ action (the "redo buffer", encoded as
unstructured `["x][v][count]body` bytes).
Plugins can only observe individual keys (vim.on_key) and high-level
effects (TextChanged, CursorMoved).
Solution:
- Users can subscribe to `CmdAtom` events to handle any user action.
- Event is deferred; handlers cannot cancel or interfere with user
actions.
- Capture `CmdSpec` from the normal/insert/visual subsystems.
- typeahead/readahead stay unstructured (`buffheader_T`): they are key
streams, not commands.
- the redo/record buffers become `StringBuilder`: fewer
allocations/copies.
- Repurpose the input/redo engine to accept `CmdSpec` objects.
"atom": one repeatable unit of user input, as a resolved (post-mapping)
keysequence plus structured fields. Only user actions, not `:normal`,
API calls, or non-"t" `feedkeys`.
BREAKING: dot-repeat of an Insert session, replays the entire session
including cursor-moves (:help ins-repeat).
BREAKING: dot-repeat of a Visual operation, replays the selection
instead of operating on a fixed-size region.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
POSIX-compatible Ex-mode requires special-cases all over the codebase to
match various quirks that don't actually matter to users.
- The main utility of *interactive* Ex-mode is its REPL behavior, and
that can be achieved with `cmdwin`, which also gains extra UX
benefits.
- The main utility of *non-interactive* `nvim -es` is for shell
scripting, where Ex-mode quirks are mostly unhelpful (e.g. the
"Entering Ex mode" message).
Solution:
- Reimplement *interactive* Ex-mode as a "persistent, insert-mode
cmdwin" in Lua.
- "nvim -e/-E" is simply an alias to "gQ".
- Reframe *non-interactive* Ex-mode (`nvim -es`) as "script mode".
- Drop POSIX Ex-mode quirks.
Improvements:
- "nvim -V1 -es" output ends with a final newline!
- "nvim -V1 -es" no longer shows the "Entering Ex mode" msg. (This was
pointless noise, unwanted for scripting purposes.)
- stdin is no longer typeahead. Scripts (":lua io.read()") can read
stdin as data.
- Empty line is a no-op: a stray blank line no longer moves the cursor
(deviates from POSIX ex "+1"), no longer exits 1 at EOF (E501).
Preserved behavior:
- cursor starts at "$"
- mode()=="cv" (for non-interactive)
- multiline commands (:append/:function/heredoc pull continuation lines)
- bare-range print
- :print=>stdout
- -V1=>stderr
- CRLF input
- continue-after-error and exit codes
Dropped (regressed) POSIX behavior (non-interactive):
- Event loop only ticks while/between commands, not while blocked
waiting for a stdin line.
- ":g/pat/visual...Q"
- input()/getchar()/":s/x/y/c" no longer consume stdin lines as
answers: Nvim stops at end-of-input, skipping the rest of the script,
exit 0. Use ":lua io.read()" instead.
- If users care about this they should use interactive Ex-mode (`gQ`).
- ":@r" stops at end of the register instead of continuing to read
cmdline input from stdin.
|
| |
|
|
|
|
|
|
|
|
| |
Problem:
This functions are most likely never used in the wild.
- They are redundant with `nvim_get_context` + `nvim_load_context`.
- The "context" concept was never fully developed and hasn't gained
traction.
Solution:
Drop these vimscript aliases, they are just extra cruft.
|
| | |
|
| |
|
|
|
|
|
|
| |
closes: vim/vim#19286
https://github.com/vim/vim/commit/dd9f7e6cbbba6d8e63cca3b4c537dc9b8cc1837a
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- :retab! line 1 and line 4 (main page heading).
- Use four columns whitespace before "by [Author]" in the user manual
heading to match the reference manual formatting.
- double space headings.
closes: vim/vim#18648
https://github.com/vim/vim/commit/542746521ff8d111f33c3c8ff298ba58966c9acc
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
vimrc (#36429)
Problem: A plugin does not know when startup scripts were already
triggered. This is useful to determine if a function is
called inside vimrc or after (like when sourcing 'plugin/'
files).
Solution: Add the v:vim_did_init variable (Evgeni Chasnovski)
closes: vim/vim#18668
https://github.com/vim/vim/commit/294bce21ee837aab209a1008d0efc3e305cf188f
Nvim has two more steps between sourcing startup scripts and loading
plugins. Set this variable after these two steps.
Co-authored-by: Evgeni Chasnovski <evgeni.chasnovski@gmail.com>
|
| |
|
|
|
| |
closes: vim/vim#18562
https://github.com/vim/vim/commit/6ab45471d4ba20ef4c954292743b9b21ec626df5
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Use double sentence spacing and wrap lines at 'textwidth'. Code
examples and tables were not wrapped unless this had already been done
locally.
closes: vim/vim#18453
https://github.com/vim/vim/commit/c58f91c035db09358c282f2a908e2aea4287785e
Fix incorrect docs in :h ModeChanged.
Cherry-pick :h bufnr() changes from patch 8.1.2080.
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
|
| |
|
|
| |
fix #34981
|
| |\ |
|