Skip to content

Commit

Permalink
Issue #32 - First attempt at implementing ci{, etc
Browse files Browse the repository at this point in the history
Can now change a region demarked by the char after ci.  Should work for
just about anything; though probably best not to use it for things other
than brackets and quotes.

Known minor bug; an empty string ci" will not work depending on which
side you are in (i.e. you won't be dropped into insert mode).

The code tries to maintain indent, with reasonable success for multiple
line curly brackets, etc.  i.e for the following command in the inner
brackets:

{
    { dsdf
    sdf <ci}>
    }
}

Results in:

{
    {
        |
    }
}

Even though Zep doesn't really support auto intent, this is convenient
for now.

ci(, ci), ci{, ci}, ci[, ci], ci", ci' are of course very useful...

No unit tests yet.
  • Loading branch information
cmaughan committed Mar 3, 2022
1 parent df2844f commit b128e5b
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 20 deletions.
1 change: 1 addition & 0 deletions include/zep/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class ZepBuffer : public ZepComponent
GlyphIterator Find(GlyphIterator start, const uint8_t* pBegin, const uint8_t* pEnd) const;
GlyphIterator FindFirstCharOf(GlyphIterator& start, const std::string& chars, int32_t& foundIndex, Direction dir) const;
GlyphIterator FindOnLineMotion(GlyphIterator start, const uint8_t* pCh, Direction dir) const;
std::pair<GlyphIterator, GlyphIterator> FindMatchingPair(GlyphIterator start, const uint8_t ch) const;
GlyphIterator WordMotion(GlyphIterator start, uint32_t searchType, Direction dir) const;
GlyphIterator EndWordMotion(GlyphIterator start, uint32_t searchType, Direction dir) const;
GlyphIterator ChangeWordMotion(GlyphIterator start, uint32_t searchType, Direction dir) const;
Expand Down
1 change: 1 addition & 0 deletions include/zep/keymap.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ DECLARE_COMMANDID(ChangeAWord)
DECLARE_COMMANDID(ChangeAWORD)
DECLARE_COMMANDID(ChangeInnerWord)
DECLARE_COMMANDID(ChangeInnerWORD)
DECLARE_COMMANDID(ChangeIn)
DECLARE_COMMANDID(ChangeToChar)

DECLARE_COMMANDID(Replace)
Expand Down
112 changes: 107 additions & 5 deletions src/buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,108 @@ GlyphIterator ZepBuffer::FindOnLineMotion(GlyphIterator start, const uint8_t* pC
return entry;
}

std::pair<GlyphIterator, GlyphIterator> ZepBuffer::FindMatchingPair(GlyphIterator start, const uint8_t ch) const
{
std::string delims;
switch (ch)
{
case '(':
case ')':
delims = "()";
break;
case '[':
case ']':
delims = "[]";
break;
case '{':
case '}':
delims = "{}";
break;
default:
// Matching same char at both ends
delims = std::string(2, ch);
break;
}

std::pair<GlyphIterator, GlyphIterator> ret;
Direction dir = Direction::Backward;

auto search = [&](GlyphIterator loc, Direction dir) {
int openCount = 1;
for (;;)
{
// Find the previous open for the current delim type
int newIndex;
loc = FindFirstCharOf(loc, delims, newIndex, dir);

// Fell off beginning, no find
if (newIndex < 0)
{
return GlyphIterator();
}

if (dir == Direction::Forward)
{
newIndex = 1 - newIndex;
}

// Match immediately for ""
if (delims[0] == delims[1])
{
newIndex = 0;
}

// Found another opener
if (newIndex == 0)
{
openCount--;
if (openCount == 0)
{
// Found opening
return loc;
}
}
// Found a closer
else if (newIndex == 1)
{
openCount++;
}

if (dir == Direction::Forward)
{
if (loc == End())
{
return GlyphIterator();
}
loc++;
}
else
{
if (loc == Begin())
{
return GlyphIterator();
}
loc--;
}
}
};

// If on the end bracket, start before it.
if (start.Char() == delims[1] && delims[0] != delims[1])
{
start--;
}

// Search for the begin
ret.first = search(start, Direction::Backward);
if (ret.first.Valid() && ret.first != End())
{
// Search for the end
ret.second = search(ret.first + 1, Direction::Forward);
}
return ret;
}

// Only works on searches of ascii characters (but navigates unicode buffer); useful for some vim operations
// Returns the index of the first found char and its location
GlyphIterator ZepBuffer::FindFirstCharOf(GlyphIterator& start, const std::string& chars, int32_t& found_index, Direction dir) const
Expand All @@ -414,7 +516,7 @@ GlyphIterator ZepBuffer::FindFirstCharOf(GlyphIterator& start, const std::string
if (!itr.Valid())
{
found_index = -1;
return itr;
return itr;
}

for (;;)
Expand All @@ -440,7 +542,7 @@ GlyphIterator ZepBuffer::FindFirstCharOf(GlyphIterator& start, const std::string
{
if (itr == Begin())
{
break;
break;
}
itr--;
}
Expand Down Expand Up @@ -1206,7 +1308,7 @@ void ZepBuffer::ClearRangeMarker(std::shared_ptr<RangeMarker> spMarker)
m_rangeMarkers.erase(spMarker->GetRange().first);
}
}

// TODO: Why is this necessary; marks the whole buffer
GetEditor().Broadcast(std::make_shared<BufferMessage>(this, BufferMessageType::MarkersChanged, Begin(), End()));
}
Expand Down Expand Up @@ -1606,11 +1708,11 @@ void ZepBuffer::BeginFlash(float seconds, FlashType flashType, const GlyphRange&
{
return;
}

auto spMarker = std::make_shared<RangeMarker>(*this);
spMarker->SetRange(ByteRange(range.first.Index(), range.second.Index()));
spMarker->SetBackgroundColor(ThemeColor::FlashColor);
spMarker->displayType = RangeMarkerDisplayType::Timed |RangeMarkerDisplayType::Background;
spMarker->displayType = RangeMarkerDisplayType::Timed | RangeMarkerDisplayType::Background;
spMarker->markerType = RangeMarkerType::Mark;
spMarker->duration = seconds;
spMarker->flashType = flashType;
Expand Down
78 changes: 63 additions & 15 deletions src/mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,18 @@ void CommandContext::GetCommandRegisters()
else
{
registers.push(keymap.RegisterName());
char reg = keymap.RegisterName();
char reg = keymap.RegisterName();

// Demote capitals to lower registers when pasting (all both)
if (reg >= 'A' && reg <= 'Z')
{
reg = (char)std::tolower((char)reg);
}
// Demote capitals to lower registers when pasting (all both)
if (reg >= 'A' && reg <= 'Z')
{
reg = (char)std::tolower((char)reg);
}

if (owner.GetEditor().GetRegisters().find(std::string({ reg })) != owner.GetEditor().GetRegisters().end())
{
pRegister = &owner.GetEditor().GetRegister(reg);
}
if (owner.GetEditor().GetRegisters().find(std::string({ reg })) != owner.GetEditor().GetRegisters().end())
{
pRegister = &owner.GetEditor().GetRegister(reg);
}
}
}

Expand Down Expand Up @@ -691,7 +691,7 @@ bool ZepMode::GetCommand(CommandContext& context)
// too specialized?
if (HandleExCommand(context.fullCommand))
{
//buffer.GetMode()->Begin(GetCurrentWindow());
// buffer.GetMode()->Begin(GetCurrentWindow());
SwitchMode(DefaultMode());
ResetCommand();
return true;
Expand Down Expand Up @@ -1629,6 +1629,54 @@ bool ZepMode::GetCommand(CommandContext& context)
context.commandResult.modeSwitch = EditorMode::Insert;
}
}
else if (mappedCommand == id_ChangeIn)
{
if (!context.keymap.captureChars.empty())
{
auto range = buffer.FindMatchingPair(bufferCursor, context.keymap.captureChars[0]);
if (range.first.Valid() && range.second.Valid())
{
if ((range.first + 1) == range.second)
{
// A closed pair (); so insert between them
GetCurrentWindow()->SetBufferCursor(range.first + 1);
context.commandResult.modeSwitch = EditorMode::Insert;
return true;
}
else
{
GlyphIterator lineEnd = context.buffer.GetLinePos(range.first, LineLocation::LineCRBegin);
if (lineEnd.Valid() && lineEnd < range.second)
{
GlyphIterator lineStart = context.buffer.GetLinePos(range.first, LineLocation::LineBegin);
auto offsetStart = (range.first.Index() - lineStart.Index());

// If change in a pair of delimeters that are on seperate lines, then
// we remove everything and replace with 2 CRs and an indent based on the start bracket
// Since Zep doesn't auto indent, this is the best we can do for now.
context.replaceRangeMode = ReplaceRangeMode::Replace;
context.op = CommandOperation::Replace;

auto offsetText = std::string(offsetStart + 4, ' ');
auto offsetBracket = std::string(offsetStart, ' ');
context.tempReg.text = std::string("\n") + offsetText + "\n" + offsetBracket;
context.pRegister = &context.tempReg;
context.beginRange = range.first + 1;
context.endRange = range.second;
context.cursorAfterOverride = range.first + (long)offsetText.length() + 2;
context.commandResult.modeSwitch = EditorMode::Insert;
}
else
{
context.beginRange = range.first + 1; // returned range is inclusive
context.endRange = range.second;
context.op = CommandOperation::Delete;
context.commandResult.modeSwitch = EditorMode::Insert;
}
}
}
}
}
else if (mappedCommand == id_SubstituteLine)
{
// Delete whole line and go to insert mode
Expand Down Expand Up @@ -1693,7 +1741,7 @@ bool ZepMode::GetCommand(CommandContext& context)
{
// Make a new end location
auto end_loc = loc;

std::string closing;
std::string opening = std::string(1, delims[findIndex]);

Expand Down Expand Up @@ -1841,7 +1889,7 @@ bool ZepMode::GetCommand(CommandContext& context)
// If not a single char, then we are trying to input a special, which isn't allowed
// TOOD: Cleaner detection of this?
// Special case for 'j + another character' which is an insert
if (true) //context.keymap.commandWithoutGroups.size() == 1 || ((context.keymap.commandWithoutGroups.size() == 2) && context.keymap.commandWithoutGroups[0] == 'j'))
if (true) // context.keymap.commandWithoutGroups.size() == 1 || ((context.keymap.commandWithoutGroups.size() == 2) && context.keymap.commandWithoutGroups[0] == 'j'))
{
context.beginRange = context.bufferCursor;
context.tempReg.text = context.keymap.commandWithoutGroups;
Expand Down Expand Up @@ -2239,7 +2287,7 @@ bool ZepMode::HandleExCommand(std::string strCommand)
}
else if (strCommand.find(":ZTestFloatSlider") == 0)
{
//auto line = buffer.GetBufferLine(bufferCursor);
// auto line = buffer.GetBufferLine(bufferCursor);
auto pSlider = std::make_shared<FloatSlider>(GetEditor(), 4);

auto spMarker = std::make_shared<RangeMarker>(buffer);
Expand All @@ -2250,7 +2298,7 @@ bool ZepMode::HandleExCommand(std::string strCommand)
}
else if (strCommand.find(":ZTestColorPicker") == 0)
{
//auto line = buffer.GetBufferLine(bufferCursor);
// auto line = buffer.GetBufferLine(bufferCursor);
auto pPicker = std::make_shared<ColorPicker>(GetEditor());
auto spMarker = std::make_shared<RangeMarker>(buffer);
spMarker->SetRange(ByteRange(bufferCursor.Index(), bufferCursor.Peek(1).Index()));
Expand Down
1 change: 1 addition & 0 deletions src/mode_vim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ void ZepMode_Vim::SetupKeyMaps()
AddKeyMapWithCountRegisters({ &m_normalMap }, { "cW" }, id_ChangeWORD);
AddKeyMapWithCountRegisters({ &m_normalMap }, { "ciw" }, id_ChangeInnerWord);
AddKeyMapWithCountRegisters({ &m_normalMap }, { "ciW" }, id_ChangeInnerWORD);
AddKeyMapWithCountRegisters({ &m_normalMap }, { "ci<.>" }, id_ChangeIn);
AddKeyMapWithCountRegisters({ &m_normalMap }, { "caw" }, id_ChangeAWord);
AddKeyMapWithCountRegisters({ &m_normalMap }, { "caW" }, id_ChangeAWORD);
AddKeyMapWithCountRegisters({ &m_normalMap }, { "C", "c$" }, id_ChangeToLineEnd);
Expand Down

0 comments on commit b128e5b

Please sign in to comment.