Skip to content
Merged
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,12 @@ Motion Command | Description
`<leader><leader> j`|Start of line forwards
`<leader><leader> k`|Start of line backwards
`<leader><leader> / <char>... <CR>`|Search n-character
`<leader><leader><leader> bdt`|Til character
`<leader><leader><leader> bdw`|Start of word
`<leader><leader><leader> bde`|End of word
`<leader><leader><leader> bdjk`|Start of line

`<leader><leader> (2s|2f|2F|2t|2T) <char><char>` are also available.
`<leader><leader> (2s|2f|2F|2t|2T) <char><char>` and `<leader><leader><leader> bd2t <char>char>` are also available.
The difference is character count required for search.
For example, `<leader><leader> 2s <char><char>` requires two characters, and search by two characters.
This mapping is not a standard mapping, so it is recommended to use your custom mapping.
Expand Down
13 changes: 5 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"url": "https://github.com/VSCodeVim/Vim/issues"
},
"engines": {
"vscode": "^1.12.0"
"vscode": "^1.13.0"
},
"categories": [
"Other",
Expand Down Expand Up @@ -422,18 +422,15 @@
},
"vim.easymotionMarkerBackgroundColor": {
"type": "string",
"description": "Set a custom background color for EasyMotion markers.",
"default": "#000000"
"description": "Set a custom background color for EasyMotion markers."
},
"vim.easymotionMarkerForegroundColorOneChar": {
"type": "string",
"description": "Set a custom color for the text on one character long markers.",
"default": "#ff0000"
"description": "Set a custom color for the text on one character long markers."
},
"vim.easymotionMarkerForegroundColorTwoChar": {
"type": "string",
"description": "Set a custom color for the text on two character long markers.",
"default": "#ffa500"
"description": "Set a custom color for the text on two character long markers."
},
"vim.easymotionMarkerWidthPerChar": {
"type": "number",
Expand Down Expand Up @@ -463,7 +460,7 @@
"vim.easymotionMarkerYOffset": {
"type": "number",
"description": "Set the Y offset of the marker text (the distance from the top).",
"default": 11
"default": 0
},
"vim.easymotionKeys": {
"type": "string",
Expand Down
57 changes: 42 additions & 15 deletions src/actions/plugins/easymotion/easymotion.cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,31 @@ import {
EasyMotionCharMoveOpions,
} from './types';

export interface EasymotionTrigger {
key: string;
leaderCount?: number;
}

export function buildTriggerKeys(trigger: EasymotionTrigger) {
return [
...Array.from({ length: trigger.leaderCount || 2 }, () => '<leader>'),
...trigger.key.split(''),
];
}

abstract class BaseEasyMotionCommand extends BaseCommand {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock];

private _baseOptions: EasyMotionMoveOptionsBase;

public abstract getMatches(position: Position, vimState: VimState): EasyMotion.Match[];

constructor(baseOptions: EasyMotionMoveOptionsBase) {
constructor(baseOptions: EasyMotionMoveOptionsBase, trigger?: EasymotionTrigger) {
super();
this._baseOptions = baseOptions;
if (trigger) {
this.keys = buildTriggerKeys(trigger);
}
}

public abstract resolveMatchPosition(match: EasyMotion.Match): Position;
Expand Down Expand Up @@ -126,12 +141,17 @@ export interface EasyMotionSearchAction {
updateSearchString(s: string): void;
getSearchString(): string;
getMatches(position: Position, vimState: VimState): EasyMotion.Match[];
readonly searchCharCount: number;
}

export class SearchByCharCommand extends BaseEasyMotionCommand implements EasyMotionSearchAction {
private _searchString: string = '';
private _options: EasyMotionCharMoveOpions;

get searchCharCount() {
return this._options.charCount;
}

constructor(options: EasyMotionCharMoveOpions) {
super(options);
this._options = options;
Expand Down Expand Up @@ -179,6 +199,10 @@ export class SearchByCharCommand extends BaseEasyMotionCommand implements EasyMo
export class SearchByNCharCommand extends BaseEasyMotionCommand implements EasyMotionSearchAction {
private _searchString: string = '';

get searchCharCount() {
return -1;
}

constructor() {
super({});
}
Expand Down Expand Up @@ -226,30 +250,34 @@ export class EasyMotionCharMoveCommandBase extends BaseCommand {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock];
private _action: EasyMotionSearchAction;

constructor(trigger: string, action: EasyMotionSearchAction) {
constructor(trigger: EasymotionTrigger, action: EasyMotionSearchAction) {
super();
this._action = action;
this.keys = ['<leader>', '<leader>', ...trigger.split('')];
this.keys = buildTriggerKeys(trigger);
}

public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState.easyMotion = new EasyMotion();
vimState.easyMotion.previousMode = vimState.currentMode;
vimState.easyMotion.searchAction = this._action;
vimState.globalState.hl = true;
// Only execute the action if easymotion is enabled
if (!Configuration.easymotion) {
return vimState;
} else {
vimState.easyMotion = new EasyMotion();
vimState.easyMotion.previousMode = vimState.currentMode;
vimState.easyMotion.searchAction = this._action;
vimState.globalState.hl = true;

vimState.currentMode = ModeName.EasyMotionInputMode;
return vimState;
vimState.currentMode = ModeName.EasyMotionInputMode;
return vimState;
}
}
}

export class EasyMotionWordMoveCommandBase extends BaseEasyMotionCommand {
private _options: EasyMotionWordMoveOpions;

constructor(trigger: string, options: EasyMotionWordMoveOpions = {}) {
super(options);
constructor(trigger: EasymotionTrigger, options: EasyMotionWordMoveOpions = {}) {
super(options, trigger);
this._options = options;
this.keys = ['<leader>', '<leader>', ...trigger.split('')];
}

public getMatches(position: Position, vimState: VimState): EasyMotion.Match[] {
Expand Down Expand Up @@ -279,10 +307,9 @@ export class EasyMotionWordMoveCommandBase extends BaseEasyMotionCommand {
export class EasyMotionLineMoveCommandBase extends BaseEasyMotionCommand {
private _options: EasyMotionMoveOptionsBase;

constructor(trigger: string, options: EasyMotionMoveOptionsBase) {
super(options);
constructor(trigger: EasymotionTrigger, options: EasyMotionMoveOptionsBase = {}) {
super(options, trigger);
this._options = options;
this.keys = ['<leader>', '<leader>', ...trigger.split('')];
}

public resolveMatchPosition(match: EasyMotion.Match): Position {
Expand Down
130 changes: 60 additions & 70 deletions src/actions/plugins/easymotion/easymotion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ export class EasyMotion {
* Caches for decorations
*/
private static decorationTypeCache: vscode.TextEditorDecorationType[] = [];
private static svgCache: { [code: string]: vscode.Uri } = {};
private static cachedBackgroundColor: string = '';
private static cachedOneFontColor: string = '';
private static cachedTwoFontColor: string = '';
private static cachedWidthPerChar: number = -1;
private static cachedHeight: number = -1;

public get markers() {
return this._markers;
Expand Down Expand Up @@ -80,60 +74,6 @@ export class EasyMotion {
}
}

/**
* Create and cache the SVG data URI for different marker codes and colors
*/
private static getSvgDataUri(
code: string,
backgroundColor: string | undefined = 'black',
fontFamily: string | undefined = 'Consolas',
fontColor: string | undefined = 'white',
fontSize: string | undefined = '14',
fontWeight: string | undefined = 'normal'
): vscode.Uri {
// Clear cache if the backgroundColor or fontColor has changed
if (this.cachedBackgroundColor !== backgroundColor) {
this.svgCache = {};
this.cachedBackgroundColor = backgroundColor;
}

if (this.cachedOneFontColor !== Configuration.easymotionMarkerForegroundColorOneChar) {
this.svgCache = {};
this.cachedOneFontColor = Configuration.easymotionMarkerForegroundColorOneChar;
}

if (this.cachedTwoFontColor !== Configuration.easymotionMarkerForegroundColorTwoChar) {
this.svgCache = {};
this.cachedTwoFontColor = Configuration.easymotionMarkerForegroundColorTwoChar;
}

const widthPerChar = Configuration.easymotionMarkerWidthPerChar;
const width = code.length * widthPerChar + 1;
const height = Configuration.easymotionMarkerHeight;

if (this.cachedWidthPerChar !== widthPerChar || this.cachedHeight !== height) {
this.svgCache = {};
this.cachedWidthPerChar = width;
this.cachedHeight = height;
}

const cache = this.svgCache[code];
if (cache) {
return cache;
} else {
const uri = vscode.Uri.parse(
`data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ` +
`${height}" height="${height}" width="${width}"><rect width="${width}" height="${height}" rx="2" ry="2" ` +
`style="fill: ${backgroundColor}"></rect><text font-family="${fontFamily}" font-size="${fontSize}" ` +
`font-weight="${fontWeight}" fill="${fontColor}" x="1" y="${Configuration.easymotionMarkerYOffset}">${code}</text></svg>`
);

this.svgCache[code] = uri;

return uri;
}
}

/**
* Clear all decorations
*/
Expand Down Expand Up @@ -244,6 +184,52 @@ export class EasyMotion {
return matches;
}

private themeColorApiSupported(): boolean {
// Theme color is available from version 1.12.
const vscodeVersionAsNumber = parseInt(vscode.version.replace(/\./g, ''), 10);
return vscodeVersionAsNumber >= 1120;
}

private getMarkerColor(
customizedValue: string,
defaultValue: string | vscode.ThemeColor,
themeColorId: string
): string | vscode.ThemeColor {
if (!this.themeColorApiSupported()) {
return customizedValue || defaultValue;
} else {
if (customizedValue) {
return customizedValue;
} else {
return new vscode.ThemeColor(themeColorId);
}
}
}

private getEasymotionMarkerBackgroundColor() {
return this.getMarkerColor(
Configuration.easymotionMarkerBackgroundColor,
'#000',
'activityBarBadge.background'
);
}

private getEasymotionMarkerForegroundColorOneChar() {
return this.getMarkerColor(
Configuration.easymotionMarkerForegroundColorOneChar,
'#f00',
'activityBarBadge.foreground'
);
}

private getEasymotionMarkerForegroundColorTwoChar() {
return this.getMarkerColor(
Configuration.easymotionMarkerForegroundColorTwoChar,
'#ffa500',
'activityBarBadge.foreground'
);
}

public updateDecorations() {
this.clearDecorations();

Expand All @@ -262,19 +248,23 @@ export class EasyMotion {

const fontColor =
keystroke.length > 1
? Configuration.easymotionMarkerForegroundColorTwoChar
: Configuration.easymotionMarkerForegroundColorOneChar;
? this.getEasymotionMarkerForegroundColorTwoChar()
: this.getEasymotionMarkerForegroundColorOneChar();

const renderOptions: vscode.ThemableDecorationInstanceRenderOptions = {
after: {
contentIconPath: EasyMotion.getSvgDataUri(
keystroke,
Configuration.easymotionMarkerBackgroundColor,
Configuration.easymotionMarkerFontFamily,
fontColor,
Configuration.easymotionMarkerFontSize,
Configuration.easymotionMarkerFontWeight
),
contentText: keystroke,
backgroundColor: this.getEasymotionMarkerBackgroundColor(),
height: `${Configuration.easymotionMarkerHeight}px`,
width: `${keystroke.length * Configuration.easymotionMarkerWidthPerChar}px`,
color: fontColor,
textDecoration: `none;
font-family: ${Configuration.easymotionMarkerFontFamily};
font-size: ${Configuration.easymotionMarkerFontSize}px;
font-weight: ${Configuration.easymotionMarkerFontWeight};
position: absolute;
z-index: 99;
bottom: ${Configuration.easymotionMarkerYOffset}px`,
},
};
// Position should be offsetted by the length of the keystroke to prevent hiding behind the gutter
Expand Down
2 changes: 1 addition & 1 deletion src/actions/plugins/easymotion/markerGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class MarkerGenerator {
private createPrefixKeyTable(): string[] {
const keyTable = this.keyTable;
const totalRemainder = Math.max(this.matchesCount - keyTable.length, 0);
const totalSteps = Math.floor(totalRemainder / keyTable.length) + 1;
const totalSteps = Math.ceil(totalRemainder / keyTable.length);
const reversed = this.keyTable.slice().reverse();
const count = Math.min(totalSteps, reversed.length);
return reversed.slice(0, count);
Expand Down
Loading