Skip to content

Instantly share code, notes, and snippets.

@t9md
Last active July 11, 2024 12:37
Show Gist options
  • Save t9md/bf6f1458de2794e214e5100f87b7fb7d to your computer and use it in GitHub Desktop.
Save t9md/bf6f1458de2794e214e5100f87b7fb7d to your computer and use it in GitHub Desktop.

I'm t9md

t9md__t9md_.png Presentation1.png

  • My editor history: Vim > Emacs > Vim > Atom
  • Emacs's limitless extensibility plant never give up sprit to me and it also made me editor addict.
  • Vim plugins: choosewin, qickhl, smalls, textmanip, vim-chef, and more
  • Atom packages: vim-mode-plus, narrow, quick-highlight, cursor-history, and more..(over 30 pkgs!).

Are you familiar with this endless editor tuning loop?.

  • Frustration > Yes I can improve it > Ya I did it!! Oh, I found another frustration point > oh, I've spent too much hours!.

What is the vim-mode-plus(VMP)?

  • It's Atom editor package which provides vim keybinding and plus more.
  • This is fork of vim-mode created by GitHub :octocat:, they now deprecated it in favor of vmp.
Version Date Event
v0.0.0 2015-08-01 fork GitHub's vim-mode
v0.1.1 2015-09-28 1st public
v1.13.0 2017-10-27 185th release in 818 days (2 years + 3 months)

AVG. 1 release / 4.5 days !!

I'm still keeping high pace release cycle as first year of vmp.


Who use vmp?

Some says vmp is the best vim emulator. IMO, maybe it's because of precise cursor placement.(e.g. cursor placement after undo or redo operation).


What I will talk today

Part1. Understand editing in Vim

  • What is editing in Vim
  • How it's implemented and executed
  • What is Motion, TextObject and Operator

Part2. vmp's unique features

  • UX improvement
  • Cursor placement
  • One key TextObject
  • Occurrence

Part1. Understand editing in Vim


What is editing in Vim?

slide-picture.jpg


Why can your j keystroke move-down cursors

slide-picture.jpg


Operation's life in stack on operationStack

slide-picture.jpg


Motion, TextObject, Operator

Motion: j, k, {, g g...

  • Move cursor from here to there.
  • When used as TARGET, select here-to-there.

TextObject: i p, i w, a W...

  • Select text range, always used as TARGET.
  • Select same range as long as cursor is within that range
    • i w select same word as long as cursor is within that word.

Operator: d, c, y...

  • Mutate TARGET
  • Always require TARGET. "I will wait until I get target!".

Summary

Editing is to transform text by mutation.

You build mutation instruction through keystroke.

Each keystroke invoke one of Operator, Motion or TextObject.

Formula: Mutation = Operator + Target(= Motion or TextObject)


Part2. VMP's unique features


:camera_with_flash: DEMO session

  • Select operator
  • TransformString operators
  • Edge motion
  • Occurrence
  • Persistent selection
  • Practical use of occurrence etc...

Flash target 🔦

  • 💡 vmp flashes an operator's target and also flashes changes on undo, redo.
  • When you type y i p, it flashes i p range with CSS keyframe animation.
  • With flash, you can be more confident for your operation.

flashOnOperate.gif


UI feedback on surround

  • 💡 Surround is difficult for me.

  • Because I have to type many keystrokes without mistake, without any UI feedback.

    • y s i w (: surround(y s) inner-word(i w) with (.
  • vmp gives proper UI feedback to make surround easy to use, easy to understand

    • Select target immediately after target was provide(i w) and before you input final surround-char.
    • When change-surround, it shows char will be change at hover indicator.
  • This GIF is after you type c s " and before you input final '

main.js


Tunable f behavior and flash find-char.

  • 💡 f is difficult. Where to stop next?.
  • With flashing finding-char, you can predict where to stop next.
  • 💡 I like all vim-seek, clever-f and vim-sneak, I want all of it's goodness.
    • Make it configurable to find across lines, number of chars to find, auto confirm by timeout, reuse f as ;...
    • You can tune vmp's f as you like.

find.gif


Eliminate unwanted cursor move

  • 💡 What if cursor is not moved after y i p?
    • In pure-Vim, cursor is moved to start of paragraph.
  • IMO, moving cursor after operator is unwanted side-effect.
  • 🎁 vmp introduces various stayOnXXX configuration and all have enabled by default.
    • In pure-Vim, stayOnVerticalMotion + stayOnDelete is doable by :set startofline=false

stayOnXXX configurations

  • Keep cursor positions after operator y i p, d d, g U i p..
    • stayOnTransformString, stayOnYank, stayOnDelete, stayOnOccurrence
  • Keep column when selecting TextObject v i p, v i i..
    • stayOnSelectTextObject
  • keep column on vertical motion g g, G...
    • stayOnVerticalMotion

One-key for VIP(Very ImPortant) TextObject

  • 🎁 You can type y p instead of y i p. How many times have you ever typed i p in your life!?
  • 🎉 Why? Some keys are freely available in operator-pending-mode.
  • ❓ Since Operator is waiting for Target(Motion or TextObject) only. Remember Operator + Target rule?.
  • 🔎 So Operator + Operator keystroke(y d, d p...) makes no sense, OK to re-map.
    • ⚠️ One exception, same-operator-twice(d d, y y...) keystrokes have special meaning.

shorthand.gif


Occurrence

  • 💡 Noticed patterns in coding. I'm repeating same operation on same keyword again and again.
  • 🎁 With occurrence, you can bulk apply operator to all occurrences of keyword within target.
  • You can edit more declaratively than imperatively, also can reduces need of . repeat.

cof.gif


Occurrence: How it works

occurrence slide-picture.jpg

Occurrence is not difficult, technically it's marker edit
  • Mark keyword under cursor then edit as in normal way.
  • You can mark keyword by either of following way
    • operator-modifier: o(word) and O(subword).(e.g. d o p, c O z)
    • preset-occurrence command: g o and g O. I'm remapping to m and M locally.
  1. Mark keyword by g o or g O(subword)
  2. Select visually
  3. c to change, I to insert at start A to append to end of occurrence.

🔰 Advice to improve vim-skill

  • 🎓 Understand this formula. Editing = Operator + Target(Motion or TextObject).

    • 📢 Install Operator + Target mental model into your brain by shouting loudly while typing.
    • y("Operator"!!) i p("Target!!")
  • 📈 Pick and master from high-return Motion, TextObject first.

    • Motion: g g, G, [, ](VMP only)
    • TextObjet: i p, i w, i z(VMP only), i ,(VMP only)
  • 🍚 Use it in daily editing.

  • 🎹 Repeat, repeat, repeat! Till your finger moves without THINKING.

    • Vim: y i p, y i p, y i p... c i p, c i p... d i p...
    • Vim: y i ,, y i ,, y i ,... c i ,, c i ,... d i ,...
    • vmp shorthand: y p, y p, y p... c p, c p... d p...
    • vmp shorthand: y ,, y ,, y ,... c ,, c ,... d ,...
  • 💉 Gradually increase TextObject, Motion of daily use.


👋: VMP is the most ambitious vim emulator in the world(IMO!).

  • 🎁 Bundled so many non-default Motion, TextObject, Operator.
  • 🎁 Introduces new editing concept: e.g. occurrence, persistent-selection.
  • ❤️ UX improvement to make Vim easy to use, easy to understand.
  • 💣 vmp is NOT aiming to become a perfect Vim emulator(no interest!).
  • 👻 My mental stance is just borrowing favorite Vim features.
  • 💡 Motivation is to experiment what-if ideas with hackable editor(Atom!).
  • 💀 Ideas comes from frustration I encounter in daily vmp-dev. Endless feedback loop!
  • I'm really SERIOUS for vmp, but vmp is NOT STRICT.
This GIF is now removed "show operator representing emoji" feature. You see how vmp is NOT strict?

emoji.gif


🙇 Thank you!!! 🙇

// NOTE: Make sure folowing "prettier-atom" option in `config.cson`.
// "prettier-atom":
// prettierOptions:
// printWidth: 80
class DemoClass {
miscFeatures() {
// [TODO] Demonstrate following unique features
// - vmp flashs TARGET on `y i p` and also flashes changes on undo and redo
// - vmp add UI feedback for surround.
// - vmp doesn't move cursor on `y i p`
// - vmp keep column on `v i p`
// - vmp's `f` is tunable to flash, to find-across-lines, to find two or more chars.
// - vmp can maximize pane by `cmd-enter`
// - vmp can `smoothScroll` by `ctrl-f,b,d,u`, `ctrl-y,e`, `z z`, `z t`, `z u`(vmp unique)
// - vmp allow you to type `y p` instead of `y i p`. one-key for VIP TextObject in operator-pending-mode
// - Available by default
// - `y p` = `y i p`
// - `y z` = `y a z`(a-fold)
// - `y c` = `y i w`
// - `y C` = `y i W`
// - `y d` = `y i d`(subword)
// - Custom keymap in my local keymap.cson
// - `y u` = `y i i`(indent)
// - `y ;` = `y i s`(inner-any-pair)
// - `y m` = `y a s`(a-any-pair)
}
moveToEdgeMotion() {
// `move-down-to-edge`, `move-up-to-edge` is like `j`, `k`, but stops at edge only.
// If next-lines column was blank, it's edge. Since you will fall if you go further.
// [TODO] Cursor is at `three`, delete `3:` to `6:`.
// [TODO] Cursor is at `three`, delete `very long` member only.
const list = [
"1: one",
"2: two",
"3: three very long",
"4: four very long",
"5: five very long",
"6: six",
]
}
// You can create `persistent-selection` by `cmd-enter`.
// persistent-selection is "dead" selection, you can revive afterwards to treat it as target.
// [TODO] Change `---..` to `xxx...` but only for index at 0, 3, 5.
persistentSelectionAndSelectOperator(one, two, three) {
const array = [
"0: ----------", // this!
"1: ----------",
"2: ----------",
"3: ----------", // this!
"4: ----------",
"5: ----------", // this!
]
}
// [TODO] Introduce occurrence
// With occurrence, you can bulk apply operator on all keywords appears in TARGET.
// - `c a f`: change a-function
// - [Normal operation] apply operator to target
// - `c o f`(g o c f): change occurrence in a-function
// - [Occurrence operation]: apply operator to occurrence in target
// [TODO] Remove temporal "element" variable, so you must replace all `element` to `this.element`.
update(mode, submode) {
const {element} = this
element.className = `${SCOPE}-${mode}`
switch (settings.get("statusBarModeStringStyle")) {
case "short":
element.textContent = (mode[0] + (submode ? submode[0] : "")
).toUpperCase()
return
case "long":
element.textContent =
LongModeStringTable[mode + (submode ? `.${submode}` : "")]
return
}
}
// [TODO] Rename `klassOrName` to `klass`
// [TODO] Rename `instance` to `object`
static getInstance(vimState, klassOrName, properties) {
const klass =
typeof klassOrName === "function"
? klassOrName
: Base.getClass(klassOrName)
const instance = new klass(vimState)
if (properties) Object.assign(instance, properties)
instance.initialize()
return instance
}
// [TODO]: Unwrap `Array.from(...)` guard. In other words, convert `Array.from(exp)` to bare `exp`.
codeFragmentsForS3() {
const [startRow, endRow] = Array.from([range.start.row, range.end.row]) // from this
// const [startRow, endRow] = [range.start.row, range.end.row] // to this
// [TODO] inline amountOfPadding (= remove use of temporal `amountOfPadding` variable)
const newRows = rows.map((rowText, i) => {
i++
const amountOfPadding = limitNumber(lastRowWidth - i.length, {
min: 0,
max: 5,
}) // extra comment
return " ".repeat(amountOfPadding) + `${i}:${rowText}`
})
}
// [TODO] Remove `Point` suffix from all identifiers in `isEdgePoint` and `isStoppablePoint` methods.
// E.g `isStoppablePoint` to `isStoppable`.
// [TODO] Inline `above`, `below`, `left`, `right` temporal variable
isEdgePoint(point) {
const abovePoint = point.translate([-1, 0])
const belowPoint = point.translate([+1, 0])
return (
this.isStoppablePoint(point) &&
(!this.isStoppablePoint(abovePoint) || !this.isStoppablePoint(belowPoint))
)
}
isStoppablePoint(point) {
const leftPoint = point.translate([0, -1])
const rightPoint = point.translate([0, +1])
return (
this.isNonWhiteSpacePoint(point) ||
this.isFirstRowOrLastRowAndStoppable(point) ||
(this.isNonWhiteSpacePoint(leftPoint) &&
this.isNonWhiteSpacePoint(rightPoint))
)
}
occurrenceIsEverywhereV1(range) {
// [TODO]: Define endPosition at __HERE__
const startPosition = range.start
// __HERE__
this.callSomeFunction({startPosition, endPosition})
}
occurrenceIsEverywhereV2(range) {
// <snip>
// [TODO]: Define foldInfo.maxIndent at __HERE__
foldInfo.minIndent = Math.min(
foldInfo.minIndent != null ? foldInfo.minIndent : indent,
indent
)
// __HERE__
}
// [TODO]: Comment out multiple `console.log` in function.
// [TODO]: Delete `console.log` included line?
consolLogScatteredForDebugPurpose(kind, rowRangeWithIndent) {
if (!foldInfoByKind[kind]) {
console.log(foldInfoByKind)
foldInfoByKind[kind] = {rowRangesWithIndent: []}
console.log(foldInfoByKind[kind])
}
const foldInfo = foldInfoByKind[kind]
console.log(foldInfo)
foldInfo.rowRangesWithIndent.push(rowRangeWithIndent)
const {indent} = rowRangeWithIndent
console.log(indent)
foldInfo.minIndent = Math.min(
foldInfo.minIndent != null ? foldInfo.minIndent : indent,
indent
)
foldInfo.maxIndent = Math.max(
foldInfo.maxIndent != null ? foldInfo.maxIndent : indent,
indent
)
console.log(foldInfo)
}
alignOccurrenceOperator() {
// [TODO] format following TWO table maually by `align` operator
// | where| move to 1st char | no move |
// |-|-|-|
// | top | `z enter` | `z t` |
// | middle | `z .` | `z z` |
// | bottom | `z -` | `z b` |
// [TODO] Align member of arrray `one`, `two`, `three` by `textC` keyword.
// prettier-ignore
const one = [
{action: "set", textC: "| A ab a Ab a"},
{action: "ensure", key: "f A", textC: " |A ab a Ab a"},
{action: "ensure", key: "f A", textC: " A ab a |Ab a"},
{action: "ensure", key: ",", textC: " |A ab a Ab a"},
{action: "ensure", key: ";", textC: " A ab a |Ab a"},
]
// prettier-ignore
const two = [
{action: "set", textC: "| a ab a cd a"},
{action: "ensure", key: "f a enter", textC: " |a ab a cd a"},
{action: "ensure", key: "f c enter", textC: " a ab a |cd a"},
]
// prettier-ignore
const three = [
{action: "set", textC: "| a ab a cd a"},
{action: "ensure", key: "f a", textC: "| a ab a cd a"},
{action: "ensure", textC: " |a ab a cd a"},
{action: "ensure", key: "f c d", textC: " a ab a |cd a"},
{action: "ensure", key: "f a", textC: " a ab a |cd a"},
{action: "ensure", textC: " a ab a cd |a"},
{action: "ensure", key: "F b", textC: " a ab a cd |a"},
{action: "ensure", textC: " a a|b a cd a"},
]
}
}
// [TODO] Add number to all TODO in this file like TODO:1, TODO:2
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Oh! You have reached enlightenment of vmp!
// Do extra TODO to delete all TODOs in this file
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment