Skip to content
Merged
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,27 @@ Plug 'lambdalisue/fall.vim'

[vim-plug]: https://github.com/junegunn/vim-plug

## Usage

Use `:Fall` command to open the fuzzy finder. The command accepts the following
arguments:

```
:Fall {source} {source_args}...
```

For example, if you'd like to use `file` source, you can use the following

```
:Fall file
```

Or `line` source with `README.md` as an argument

```
:Fall line README.md
```

## Similar Projects

- [ddu.vim](https://github.com/Shougo/ddu.vim)<br>A highly customizable and
Expand Down
19 changes: 19 additions & 0 deletions autoload/fall.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function! fall#start(name, args, options) abort
call denops#plugin#wait_async(
\ 'fall',
\ { -> denops#notify('fall', 'start', [a:name, a:args, a:options]) },
\)
endfunction

function! fall#command(cmdargs) abort
let l:args = split(a:cmdargs, ' ', v:true)
const l:name = remove(l:args, 0)
call fall#start(l:name, l:args, {})
endfunction

function! fall#action(name) abort
call denops#plugin#wait_async(
\ 'fall',
\ { -> denops#notify('fall', 'dispatch', ['action-invoke', a:name]) },
\)
endfunction
17 changes: 17 additions & 0 deletions autoload/fall/internal/mapping.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
let s:saved_maps = []

function! fall#internal#mapping#store() abort
if !empty(s:saved_maps)
return
endif
let s:saved_maps = map(maplist(), { _, m -> m.mode == 'c' })
endfunction

function! fall#internal#mapping#restore() abort
for l:m in s:saved_maps
try
call mapset(l:m)
catch
endtry
endfor
endfunction
28 changes: 28 additions & 0 deletions denops/@fall-builtin/actions/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Action } from "https://deno.land/x/[email protected]/mod.ts";
import { batch } from "https://deno.land/x/[email protected]/batch/mod.ts";
import { assert, is } from "https://deno.land/x/[email protected]/mod.ts";

const isOptions = is.StrictOf(is.PartialOf(is.ObjectOf({})));

export function getAction(
options: Record<string, unknown>,
): Action {
assert(options, isOptions);
return {
invoke: async (denops, { cursorItem, selectedItems }) => {
const items = selectedItems.length > 0
? selectedItems
: cursorItem
? [cursorItem]
: [];
const content = items.map((item) => JSON.stringify(item));
await batch(denops, async (denops) => {
for (const line of content) {
await denops.cmd(`echomsg ${line}`);
}
});
// Keep picker running
return true;
},
};
}
69 changes: 69 additions & 0 deletions denops/@fall-builtin/actions/open.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Action } from "https://deno.land/x/[email protected]/mod.ts";
import * as buffer from "https://deno.land/x/[email protected]/buffer/mod.ts";
import * as fn from "https://deno.land/x/[email protected]/function/mod.ts";
import { assert, is } from "https://deno.land/x/[email protected]/mod.ts";

const isOptions = is.StrictOf(is.PartialOf(is.ObjectOf({
bang: is.Boolean,
mods: is.String,
cmdarg: is.String,
opener: is.String,
splitter: is.String,
})));

const isPathDetail = is.ObjectOf({
path: is.String,
line: is.OptionalOf(is.Number),
column: is.OptionalOf(is.Number),
});

export function getAction(
options: Record<string, unknown>,
): Action {
assert(options, isOptions);
const bang = options.bang ?? false;
const mods = options.mods ?? "";
const cmdarg = options.cmdarg ?? "";
const firstOpener = options.opener ?? "edit";
const splitter = options.splitter ?? firstOpener;
return {
invoke: async (denops, { cursorItem, selectedItems }) => {
const items = selectedItems.length > 0
? selectedItems
: cursorItem
? [cursorItem]
: [];
let opener = firstOpener;
for (const item of items) {
if (!isPathDetail(item.detail)) {
continue;
}
try {
const info = await buffer.open(denops, item.detail.path, {
bang,
mods,
cmdarg,
opener,
});
opener = splitter;
if (item.detail.line || item.detail.column) {
const line = item.detail.line ?? 1;
const column = item.detail.column ?? 1;
await fn.win_execute(
denops,
info.winid,
`silent! call cursor(${line}, ${column})`,
);
}
} catch (err) {
// Fail silently
console.debug(
`[fall] (action/open) Failed to open ${item.detail.path}:`,
err,
);
}
}
return false;
},
};
}
73 changes: 73 additions & 0 deletions denops/@fall-builtin/actions/quickfix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { Action } from "https://deno.land/x/[email protected]/mod.ts";
import * as fn from "https://deno.land/x/[email protected]/function/mod.ts";
import { assert, is } from "https://deno.land/x/[email protected]/mod.ts";

const isOptions = is.StrictOf(is.PartialOf(is.ObjectOf({
what: is.PartialOf(is.ObjectOf({
context: is.Unknown,
id: is.Number,
idx: is.UnionOf([is.Number, is.String]),
nr: is.Number,
title: is.String,
})),
action: is.LiteralOneOf(["a", "r", "f", " "] as const),
target: is.LiteralOneOf(
[
"selected-or-cursor",
"selected-or-processed",
] as const,
),
})));

const isPathDetail = is.ObjectOf({
path: is.String,
line: is.OptionalOf(is.Number),
column: is.OptionalOf(is.Number),
});

function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}

export function getAction(
options: Record<string, unknown>,
): Action {
assert(options, isOptions);
const what = options.what ?? {};
const action = options.action ?? " ";
const target = options.target ?? "selected-or-cursor";
return {
invoke: async (denops, { cursorItem, selectedItems, processedItems }) => {
const source = selectedItems.length > 0
? selectedItems
: target === "selected-or-cursor"
? cursorItem ? [cursorItem] : []
: processedItems;
const items = source
.map((item) => {
if (isPathDetail(item.detail)) {
return {
filename: item.detail.path,
lnum: item.detail.line,
col: item.detail.column,
};
}
return undefined;
})
.filter(isDefined);
try {
await fn.setqflist(denops, [], action, {
...what,
items,
});
} catch (err) {
// Fail silently
console.debug(
`[fall] (action/quickfix) Failed to set quickfix list:`,
err,
);
}
return false;
},
};
}
77 changes: 77 additions & 0 deletions denops/@fall-builtin/previewers/path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { Previewer } from "https://deno.land/x/[email protected]/mod.ts";
import { batch } from "https://deno.land/x/[email protected]/batch/mod.ts";
import * as fn from "https://deno.land/x/[email protected]/function/mod.ts";
import { basename } from "https://deno.land/[email protected]/path/basename.ts";
import { assert, is } from "https://deno.land/x/[email protected]/mod.ts";

const isOptions = is.StrictOf(is.PartialOf(is.ObjectOf({})));

const isPathDetail = is.ObjectOf({
path: is.String,
line: is.OptionalOf(is.Number),
column: is.OptionalOf(is.Number),
});

export function getPreviewer(
options: Record<string, unknown>,
): Previewer {
assert(options, isOptions);
return {
preview: async (denops, item, { winid }) => {
if (!isPathDetail(item.detail)) {
// No preview is available
return;
}
const { line = 1, column = 1 } = item.detail;
const path = await fn.fnameescape(denops, item.detail.path);
await batch(denops, async (denops) => {
await fn.win_execute(
denops,
winid,
`setlocal modifiable`,
);
await fn.win_execute(
denops,
winid,
`silent! 0,$delete _`,
);
await fn.win_execute(
denops,
winid,
`silent! 0read ${path}`,
);
await fn.win_execute(
denops,
winid,
`silent! $delete _`,
);
await fn.win_execute(
denops,
winid,
`silent! 0file`,
);
await fn.win_execute(
denops,
winid,
`silent! syntax clear`,
);
await fn.win_execute(
denops,
winid,
`silent! file fall://preview/${basename(path)}`,
);
await fn.win_execute(
denops,
winid,
`silent! doautocmd <nomodeline> BufRead`,
);
await fn.win_execute(
denops,
winid,
`setlocal nomodifiable`,
);
await fn.win_execute(denops, winid, `normal! ${line}G${column}|`);
});
},
};
}
32 changes: 32 additions & 0 deletions denops/@fall-builtin/processors/lexical.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type {
Processor,
ProcessorItem,
} from "https://deno.land/x/[email protected]/mod.ts";
import { assert, is } from "https://deno.land/x/[email protected]/mod.ts";

const isOptions = is.StrictOf(is.PartialOf(is.ObjectOf({
reverse: is.Boolean,
})));

export function getProcessor(
options: Record<string, unknown>,
): Processor {
assert(options, isOptions);
const alpha = options.reverse ? -1 : 1;
return {
getStream: (_denops) => {
const items: ProcessorItem[] = [];
return new TransformStream({
transform(chunk) {
items.push(chunk);
},
flush(controller) {
items.sort((a, b) => {
return a.value.localeCompare(b.value) * alpha;
});
items.forEach((item) => controller.enqueue(item));
},
});
},
};
}
Loading