Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions config.def.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ static const KeyBinding bindings_operators[] = {
{ "'", ACTION(MARK) },
{ "c", ACTION(OPERATOR_CHANGE) },
{ "d", ACTION(OPERATOR_DELETE) },
{ "g~", ALIAS("<vis-prompt-show>|tr '[:lower:][:upper:]' '[:upper:][:lower:]'<Enter>") },
{ "gu", ALIAS("<vis-prompt-show>|awk '{printf \"%s\", tolower($0)}'<Enter>")},
{ "gU", ALIAS("<vis-prompt-show>|awk '{printf \"%s\", toupper($0)}'<Enter>")},
{ "g~", ACTION(SELECTIONS_CASE_TOGGLE) },
{ "gu", ACTION(SELECTIONS_CASE_TOLOWER) },
{ "gU", ACTION(SELECTIONS_CASE_TOUPPER) },
{ "p", ACTION(PUT_AFTER) },
{ "P", ACTION(PUT_BEFORE) },
{ "y", ACTION(OPERATOR_YANK) },
Expand Down
76 changes: 76 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ static Vis vis[1];
X(ka_selections_align, SELECTIONS_ALIGN, 0, "vis-selections-align", "Try to align all selections on the same column") \
X(ka_selections_align_indent, SELECTIONS_ALIGN_INDENT_LEFT, .i = -1, "vis-selections-align-indent-left", "Left-align all selections by inserting spaces") \
X(ka_selections_align_indent, SELECTIONS_ALIGN_INDENT_RIGHT, .i = +1, "vis-selections-align-indent-right", "Right-align all selections by inserting spaces") \
X(ka_selections_case, SELECTIONS_CASE_TOUPPER, .i = +1, "vis-selections-case-toupper", "Uppercase all selections") \
X(ka_selections_case, SELECTIONS_CASE_TOGGLE, .i = 0, "vis-selections-case-toggle", "Toggle case of all selections") \
X(ka_selections_case, SELECTIONS_CASE_TOLOWER, .i = -1, "vis-selections-case-tolower", "Lowercase all selections") \
X(ka_selections_clear, SELECTIONS_REMOVE_ALL, 0, "vis-selections-remove-all", "Remove all but the primary selection") \
X(ka_selections_complement, SELECTIONS_COMPLEMENT, 0, "vis-selections-complement", "Complement selections") \
X(ka_selections_intersect, SELECTIONS_INTERSECT, 0, "vis-selections-intersect", "Intersect with selections from mark") \
Expand Down Expand Up @@ -350,6 +353,79 @@ static KEY_ACTION_FN(ka_selections_align_indent)
return keys;
}

static KEY_ACTION_FN(ka_selections_case)
{
Text *txt = vis_text(vis);
View *view = vis_view(vis);
char *buf;
wchar_t *wcs;
for (Selection *s = view_selections(view), *next; s; s = next) {
next = view_selections_next(s);
Filerange sel = view_selections_get(s);
if (!text_range_valid(sel))
continue;

size_t mblen = text_range_size(sel);
// We know that the amount of wide-characters required to hold the
// selection is at MOST the amount of bytes in the selection.

wcs = malloc(mblen * sizeof(*wcs) + mblen + 1);
if (wcs == NULL) {
return keys;
}

buf = (char*)wcs + mblen * sizeof(*wcs);
buf[mblen] = 0;

text_bytes_get(txt, sel.start, mblen, buf);

// This is safe as long as the multibyte string is 0-terminated.
size_t wcslen = mbstowcs(wcs, buf, mblen);
if (wcslen == (size_t) -1) {
goto next_sel;
}

for (size_t i = 0; i < wcslen; i++) {
wchar_t *wp = wcs + i;
wint_t wc = (wint_t)*wp;
switch(arg->i) {
case -1:{
wc = towlower(wc);
}break;
case 0:{
if (iswlower(wc))
wc = towupper(wc);
else
wc = towlower(wc);
}break;
case 1:{
wc = towupper(wc);
}break;
}
*wp = wc;
}

// We assume that the number of bytes required by the modified
// wide-character string is the same as the multibyte input.
if (wcstombs(buf, wcs, mblen) == (size_t) -1) {
goto next_sel;
}

if (!text_delete_range(txt, sel))
goto next_sel;
if (!text_insert(vis, txt, sel.start, buf, mblen))
goto next_sel;

view_selections_set(s, sel);

next_sel:
free(wcs);
}

vis_draw(vis);
return keys;
}

static KEY_ACTION_FN(ka_selections_clear)
{
View *view = vis_view(vis);
Expand Down
Loading