Comments (12)
Hi,
On the subject of braces; it would be nice to bind a key so the cursor jumps to the matching brace (if the cursor is on a brace), or finds the next unpaired brace if the cursor is not on a brace. e.g.
"Ctrl-J": "JumpToMatchingBrace | JumpToNextUnpairedBrace",
A JumpToNextUnpairedBrace
action would be the basis for a selectinner
function (CTRL-Shift-I) , and the trivial extension to a selectouter
function (CTRL-Shift-J).
Kind Regards Gavin Holt
from micro.
I remember we were investigating the reasons for this "feature"... Here is a summary: #2876 (comment)
from micro.
Hi,
Thank you for pointing me to the previous discussions.
From my perspective matchbrace
highlighting option and JumpToMatchingBrace
action should only apply to the character under the (block) cursor (i.e to the right of the I beam cursor).
I would really value a JumpToNextUnpairedBrace
action, to aid selecting all the contents of braces (including any nested braces).
Not wanting to impose upon others - could these be written in Lua? The only drawback would be that a Lua function would not work well with multiple cursors so built-in actions may be better.
Kind Regards Gavin Holt
from micro.
I don't quite understand what exact behavior of JumpToNextUnpairedBrace
do you want. Could you explain it with some examples?
For example, if the text is (foo(bar)baz)
, to which brace should JumpToNextUnpairedBrace
jump if the cursor is on foo
, if it is on bar
and if it is on baz
?
from micro.
Hi
I really want a selectinner
action, which I believe is dependent upon the ability to JumpToNextUnpairedBrace
.
For your test string, (foo(bar)baz) a selectinner
action would select the content of the nested brace if the (block) cursor was on any italic character, or select the whole brace contents if the (block) cursor was on a bold character.
For example:
- If the cursor was on the
f
,o
, secondb
, seconda
or thez
the resulting selection would befoo(bar)baz
. - If the cursor was on the first
b
, firsta
or ther
the resulting selection would bebar
. - If the cursor was on a bold brace the resulting selection would be
foo(bar)baz
. - If the cursor was on an italic brace the resulting selection would be
bar
.
To achieve this I would first search for the next unpaired brace, save this location, then jump to matching brace, then select from this location to the saved location - excluding the limiting braces.
Searching for the next unpaired brace would require counting opening/closing braces (by type), and finding the first closing brace without a preceding opening brace. If the cursor is initially on a closing brace then this is the found position and the cursor does not move forward.
A selectouter
action would be very similar but include the limiting braces in the selection.
Kind Regards Gavin Holt
from micro.
Hi
I would plan to use all of these new actions, in key bindings:
"CtrlJ": " JumpToMatchingBrace | JumpToNextUnpairedBrace ",
"CtrlShiftJ": "selectinner",
With easy shortcuts to navigate braces - we might even attract some Lisp coders. 😊
Kind Regards Gavin Holt
from micro.
Got it. So, actually, it would be not just one new action JumpToNextUnpairedBrace
but two separate actions, since it makes difference in which direction to jump. Precisely, it would be like:
JumpToNextUnpairedBraceLeft
: jump to the nearest opening brace to the left of the cursor, that has no matching closing brace that is also to the left of the cursor.JumpToNextUnpairedBraceRight
: jump to the nearest closing brace to the right of the cursor, that has no matching opening brace that is also to the right of the cursor.
If we forget for a moment about those actions and focus just on your selectinner
, its algorithm would be like:
- If the cursor in on a brace: try to find a matching brace, and if found, select the text between the two braces.
- Otherwise:
2.1. Try to find the nearest opening brace to the left of the cursor, that has no matching closing brace that is also to the left of the cursor.
2.2. If found, try to find a maching brace for it, and if found, select the text between the two braces.
I might try to code up some Lua code doing that for you, once I have some time... Or in the meantime, I might suggest you try using Loc's Move() method:
the next character to the right of the given location:
loc:Move(1, bp.Buf)
or the next character to the left of it:
loc:Move(-1, bp.Buf)
It automatically takes care of whether this next character is on the same line or on the next/previous line.
You might also look how SelectWord() is implemented, and try to do something similar (unless you already did?).
from micro.
Ok... I think what you need for your selectinner
and selectouter
is not Jump*
actions but FindMatchingBrace() function. For example, if you found an "unpaired" brace, you shouldn't jump to it right away, you should first check if the matching brace for it exists (otherwise, there is nothing to select, right?). So you could use FindMatchingBrace()
to find the matching brace for the given location without jumping to it, and regardless of the current cursor location.
But, FindMatchingBrace()
is very inconvenient to use from Lua. But I have some ideas how to rework its interface and implementation to streamline its usage:
- Instead of passing a single brace pair to
FindMatchingBrace()
, make it traverse all brace pairs in BracePairs on its own. - Add a boolean parameter to
FindMatchingBrace()
for disabling the "I-beam" behavior, i.e. for finding the matching brace always for the brace precisely at the given location, not one character left of it.
As a bonus, it would allow to fix the minor issue I described in #2876 (comment), since it would allow FindMatchingBrace()
to prioritize between different braces.
from micro.
So... I've uploaded #3319 which improves FindMatchingBrace()
almost like I suggested above. With this PR you can use the following custom Lua action, instead of JumpToMatchingBrace
, to jump exactly to the matching brace for the current cursor location (e.g. without the I-beam behavior):
function jumpToExactMatchingBrace(bp)
local mb, left, found = bp.Buf:FindMatchingBrace(-bp.Cursor.Loc)
if found and not left then
bp.Cursor:GotoLoc(mb)
bp:Relocate()
return true
end
return false
end
Then, use can also use these functions for finding the next "unpaired" brace to the left or to the right:
function findNextUnpairedBraceLeft(bp)
local countPar = 0
local countCurl = 0
local countSq = 0
local loc = -bp.Cursor.Loc
local bufStart = bp.Buf:Start()
while loc:GreaterThan(bufStart) do
loc = loc:Move(-1, bp.Buf)
local curLine = bp.Buf:Line(loc.Y)
local r = util.RuneAt(curLine, loc.X)
if r == "(" then
countPar = countPar + 1
if countPar > 0 then
return loc
end
elseif r == "{" then
countCurl = countCurl + 1
if countCurl > 0 then
return loc
end
elseif r == "[" then
countSq = countSq + 1
if countSq > 0 then
return loc
end
elseif r == ")" then
countPar = countPar - 1
elseif r == "}" then
countCurl = countCurl - 1
elseif r == "]" then
countSq = countSq - 1
end
end
return nil
end
function findNextUnpairedBraceRight(bp)
local countPar = 0
local countCurl = 0
local countSq = 0
local loc = -bp.Cursor.Loc
local bufEnd = bp.Buf:End()
while loc:LessThan(bufEnd) do
loc = loc:Move(1, bp.Buf)
local curLine = bp.Buf:Line(loc.Y)
local r = util.RuneAt(curLine, loc.X)
if r == ")" then
countPar = countPar + 1
if countPar > 0 then
return loc
end
elseif r == "}" then
countCurl = countCurl + 1
if countCurl > 0 then
return loc
end
elseif r == "]" then
countSq = countSq + 1
if countSq > 0 then
return loc
end
elseif r == "(" then
countPar = countPar - 1
elseif r == "{" then
countCurl = countCurl - 1
elseif r == "[" then
countSq = countSq - 1
end
end
return nil
end
And you use them to implement the actual actions for jumping to the next "unpaired" brace:
function jumpToNextUnpairedBraceLeft(bp)
local loc = findNextUnpairedBraceLeft(bp)
if loc ~= nil then
bp.Cursor:GotoLoc(loc)
bp:Relocate()
return true
end
return false
end
function jumpToNextUnpairedBraceRight(bp)
local loc = findNextUnpairedBraceRight(bp)
if loc ~= nil then
bp.Cursor:GotoLoc(loc)
bp:Relocate()
return true
end
return false
end
Finally, you can implement selectin
and selectout
:
function selectin(bp)
local braceStart = findNextUnpairedBraceLeft(bp)
if braceStart ~= nil then
local braceEnd, left, found = bp.Buf:FindMatchingBrace(braceStart)
if found and not left then
braceStart = braceStart:Move(1, bp.Buf)
if braceStart:LessThan(braceEnd) then
bp.Cursor:SetSelectionStart(braceStart)
bp.Cursor:SetSelectionEnd(braceEnd)
bp.Cursor.OrigSelection = -bp.Cursor.CurSelection
bp.Cursor:GotoLoc(braceEnd)
bp:Relocate()
return true
end
end
end
return false
end
function selectout(bp)
local braceStart = findNextUnpairedBraceLeft(bp)
if braceStart ~= nil then
local braceEnd, left, found = bp.Buf:FindMatchingBrace(braceStart)
if found and not left then
braceEnd = braceEnd:Move(1, bp.Buf)
if braceStart:LessThan(braceEnd) then
bp.Cursor:SetSelectionStart(braceStart)
bp.Cursor:SetSelectionEnd(braceEnd)
bp.Cursor.OrigSelection = -bp.Cursor.CurSelection
bp.Cursor:GotoLoc(braceEnd)
bp:Relocate()
return true
end
end
end
return false
end
from micro.
Hi,
Many thanks, I will enjoy studying these new functions.
My investigations will need to wait for PR merge and then Nightly binary release.
Meantime I have some generic goto/select functions using regex to share:
function selectforwards(Current)
-- Select forwards from cursor using regex (set to ungreedy and case insensitive)
-- this does require escaping of regex metacharacter
-- this does not pollute the find history
-- this does not highlight all matches
local prompt = "SelectUntil : "
local seed = "(?sU-i).*"
local history = "SelectUntil"
micro.InfoBar():Prompt(prompt, seed, history,
function(input)
return
end,
function(input, canceled)
if input and not canceled then
local top = Current.Buf:Start()
local bottom = Current.Buf:End()
local searchLoc = -Current.Cursor.Loc
local down = true
local useRegex = true
local res, found = Current.Buf:FindNext(input, top, bottom, searchLoc, down, useRegex)
if found then
Current.Cursor:SetSelectionStart(searchLoc)
Current.Cursor:SetSelectionEnd(res[2])
Current.Cursor.OrigSelection[1] = -Current.Cursor.CurSelection[1]
Current.Cursor.OrigSelection[2] = -Current.Cursor.CurSelection[2]
Current.Cursor:GotoLoc(res[2])
Current:Relocate()
end
end
end
)
end
function selectbackwards(Current)
-- Select backwards from cursor using regex (set to ungreedy and case insensitive)
-- this does require escaping of regex metacharacter
-- this does not pollute the find history
-- this does not highlight all matches
local prompt = "SelectBack : "
local seed = "(?sU-i)"
local history = "SelectBack"
micro.InfoBar():Prompt(prompt, seed, history,
function(input)
return
end,
function(input, canceled)
if input and not canceled then
local top = Current.Buf:Start()
local bottom = Current.Buf:End()
local searchLoc = -Current.Cursor.Loc
local down = false
local useRegex = true
local res, found = Current.Buf:FindNext(input, top, bottom, searchLoc, down, useRegex)
if found then
Current.Cursor:SetSelectionStart(searchLoc)
Current.Cursor:SetSelectionEnd(res[1])
Current.Cursor.OrigSelection[1] = -Current.Cursor.CurSelection[1]
Current.Cursor.OrigSelection[2] = searchLoc
Current.Cursor:GotoLoc(res[1])
Current:Relocate()
end
end
end
)
end
function goforwards(Current)
-- Move forwards from cursor using regex (set to ungreedy and case insensitive)
-- this does require escaping of regex metacharacter
-- this does not pollute the find history
-- this does not highlight all matches
-- this will alight at the beginning of a multicharacter search
local prompt = "Go forwards : "
local seed = "(?sU-i)"
local history = "Goforwards"
micro.InfoBar():Prompt(prompt, seed, history,
function(input)
return
end,
function(input, canceled)
if input and not canceled then
local top = Current.Buf:Start()
local bottom = Current.Buf:End()
local searchLoc = -Current.Cursor.Loc
local down = true
local useRegex = true
local res, found = Current.Buf:FindNext(input, top, bottom, searchLoc, down, useRegex)
if found then
Current.Cursor:SetSelectionStart(res[1])
Current.Cursor:SetSelectionEnd(res[2])
Current.Cursor.OrigSelection[1] = -Current.Cursor.CurSelection[1]
Current.Cursor.OrigSelection[2] = -Current.Cursor.CurSelection[2]
Current.Cursor:GotoLoc(res[2])
Current:Relocate()
Current.Cursor:Deselect(true)
end
end
end
)
end
function gobackwards(Current)
-- Move backwards from cursor using regex (set to ungreedy and case insensitive)
-- this does require escaping of regex metacharacter
-- this does not pollute the find history
-- this does not highlight all matches
-- this will alight at the beginning of a multicharacter search
local prompt = "Go backwards : "
local seed = "(?sU-i)"
local history = "Gobackwards"
micro.InfoBar():Prompt(prompt, seed, history,
function(input)
return
end,
function(input, canceled)
if input and not canceled then
local top = Current.Buf:Start()
local bottom = Current.Buf:End()
local searchLoc = -Current.Cursor.Loc
local down = false
local useRegex = true
local res, found = Current.Buf:FindNext(input, top, bottom, searchLoc, down, useRegex)
if found then
Current.Cursor:SetSelectionStart(res[1])
Current.Cursor:SetSelectionEnd(res[2])
Current.Cursor.OrigSelection[1] = -Current.Cursor.CurSelection[1]
Current.Cursor.OrigSelection[2] = -Current.Cursor.CurSelection[2]
Current.Cursor:GotoLoc(res[2])
Current:Relocate()
Current.Cursor:Deselect(true)
end
end
end
)
end
I find these functions very useful, much quicker than reaching for the mouse.
Kind Regards Gavin Holt
PS I will leave this open as a point for discussion - until the PR is merged
from micro.
FYI #3319 merged.
from micro.
Hi,
New binary + your functions work beautifully.
Many Thanks
Kind Regards Gavin Holt
from micro.
Related Issues (20)
- disable error HOT 2
- Confusion over missing bindings
- Differing copy-paste mechanics between tmux and non-tmux HOT 2
- Calling clipboard.Write in lua crashes micro HOT 6
- 2.0.13 micro-2.0.13-linux-arm.tar.gz may have been tampered with HOT 12
- Micro panics when cd-ing to root
- How to toggle softwrap + wordwrap in editor, and how to make it a default setting? HOT 2
- Colors in Color Schems appear wrong. HOT 2
- DeleteLine does not delete selected lines HOT 8
- Micro reindents settings.json files with spaces on open HOT 1
- Nightly builds are not the current master branch 😭 HOT 3
- Binding Backspace related keys do not work on Windows HOT 1
- Autosave locally HOT 22
- Scrolling does not always go down with the cursor HOT 2
- Cannot click to move cursor to first character of document HOT 6
- Panic with `open folder\` on Windows HOT 1
- Empty line for no reason HOT 3
- [Feature request] Proposal of functionality improvement HOT 7
- [Bug] Overwriting of configuration file HOT 1
- Detect and display keyboard shortcuts? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from micro.