Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
danielo515 committed Mar 2, 2023
1 parent 4900e4d commit e995ef3
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 26 deletions.
2 changes: 1 addition & 1 deletion haxelib.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"externs"
],
"description": "Create neovim plugins using Haxe",
"version": "0.1.2",
"version": "0.2.0",
"classPath": "src/",
"releasenote": "properly working as a lib",
"contributors": [
Expand Down
66 changes: 50 additions & 16 deletions src/TableWrapper.hx
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
// From https://try.haxe.org/#0D7f4371
// From"" https://try.haxe.org/#0D7f4371
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;

using haxe.macro.TypeTools;
using haxe.macro.ExprTools;
using Safety;
using haxe.macro.ComplexTypeTools;

var uniqueCount = 1;
#end

/**
Generates a new array that does not contain duplicate values,
giving preference to the leftmos elements.
*/
function uniqueValues< T >(array:Array< T >, indexer:(T) -> String) {
final index = new haxe.ds.StringMap< Bool >();
return [for (val in array) {
final key = indexer(val);
if (index.exists(key))
continue;
index.set(key, true);
val;
}];
}

// Class that transforms any Haxe object into a plain lua table
// Thanks to @kLabz
#if macro
Expand All @@ -32,11 +50,14 @@ public static function followTypesUp(arg:haxe.macro.Type) {
}
}

static function extractObjFields(objExpr) {
public static function extractObjFields(objExpr) {
return switch Context.getTypedExpr(Context.typeExpr(objExpr)).expr {
case EObjectDecl(inputFields):
var inputFields = inputFields.copy();
{fieldExprs: [for (f in inputFields) f.field => f.expr], inputFields: inputFields};
{
fieldExprs: [for (f in inputFields) f.field => f.expr],
inputFields: inputFields
};

case _:
throw "Must be called with an anonymous object";
Expand Down Expand Up @@ -83,8 +104,13 @@ static function objToTable(obj:Expr):Expr {
final inputFields = x.inputFields;
final fieldExprs = x.fieldExprs;

var objFields:Array< ObjectField > = [for (f in fields) {
final currentFieldExpression = fieldExprs.get(f.name);
var generatedFields:Array< ObjectField > = [for (f in fields) {
final currentFieldExpression = fieldExprs.get(f.name).or(macro $v{null});

// trace("currentFieldExpression", currentFieldExpression);
if (currentFieldExpression == null) {
continue;
}
switch (f.type) {
case _.toComplexType() => macro :Array< String > :{
field:f.name,
Expand All @@ -104,23 +130,31 @@ static function objToTable(obj:Expr):Expr {
{field: f.name, expr: macro(TableWrapper.fromExpr(${fieldExprs.get(f.name)}) : $ct)};

case TAnonymous(_):
{field: f.name, expr: objToTable(fieldExprs.get(f.name))};
// case TInst(_.get().name => "Array", [TAnonymous(_)]):
// trace(currentFieldExpression.toString());
// {
// field: f.name,
// expr: macro lua.Table.create(${ExprTools.map(currentFieldExpression, objToTable)})
// }
{field: f.name, expr: objToTable(currentFieldExpression)};

case TAbstract(_, _) | TFun(_, _):
{field: f.name, expr: (fieldExprs.get(f.name))};
{field: f.name, expr: (currentFieldExpression)};
case _:
{field: f.name, expr: objToTable(fieldExprs.get(f.name))};
{field: f.name, expr: objToTable(currentFieldExpression)};
}
}];

var inputObj = {expr: EObjectDecl(inputFields), pos: ex.pos};
var obj = {expr: EObjectDecl(objFields), pos: ex.pos};
// We merge the generated fields, which may include fields that are not present in the code
// (in case of optional fields, for example)
// with the real fields so the type checking is accurate and does not allow extra fields that
// will be silently ignored otherwise
final obj = {expr: EObjectDecl(generatedFields), pos: ex.pos};

final inputObj = {
expr: EObjectDecl(uniqueValues(inputFields.concat(generatedFields), f -> f.field)),
pos: ex.pos
};

// trace("\n=========\n", complexType.toString());
// trace("fields", inputFields);
// trace("ex", ex);
// trace("fieldExprs", fieldExprs);
// trace("obj", obj);

final name = '_dce${uniqueCount++}';
return macro @:mergeBlock {
Expand Down
50 changes: 50 additions & 0 deletions src/packer/Macro.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package packer;

import haxe.macro.Context;
import haxe.macro.Expr;

using haxe.macro.TypeTools;
using haxe.macro.ExprTools;
using Safety;
using haxe.macro.ComplexTypeTools;

/**
Gets the values of the given object expression and
turns it into an untyped lua call with the values in the right order.
like this:
```
// plugin({1, 2, a=3})
untyped __lua__("{ {0}, {1}, a={2} }", 1, 2, 3)
```
*/
macro function plugin(expr:Expr) {
final expected = Context.getExpectedType();
switch (expected) {
case TType(t, params):
final values = TableWrapper.extractObjFields(expr);
var idx = 0;
final lala = [for (k => v in values.fieldExprs) {
{str: '$k = {${idx++}}, ', expr: v};
}];
final lolo = lala.fold(
(item, acc:{str:String, expr:Array< Expr >}) -> {
acc.str += item.str;
acc.expr.push(item.expr);
return acc;
},
{str: "", expr: []}
);
// We need to split this last thing like this because we need to get
// an array of Expr first so reification converts
// the resulting array to the arguments of the __lua__ call
final args = [macro $v{'{ ${lolo.str} }'}].concat(lolo.expr);
// This is the actual wanted output
final out = macro untyped __lua__($a{args});
trace(out.toString());
return out;
case _:
trace("other");
};

return null;
}
184 changes: 184 additions & 0 deletions src/packer/Packer.hx
Original file line number Diff line number Diff line change
@@ -1,12 +1,86 @@
package packer;

import vim.VimTypes.LuaArray;
import vim.Vim;
import lua.Table.create as t;
import plenary.Job;

/**
PluginSpec type reference:
{
'myusername/example', -- The plugin location string
-- The following keys are all optional
disable = boolean, -- Mark a plugin as inactive
as = string, -- Specifies an alias under which to install the plugin
installer = function, -- Specifies custom installer. See "custom installers" below.
updater = function, -- Specifies custom updater. See "custom installers" below.
after = string or list, -- Specifies plugins to load before this plugin. See "sequencing" below
rtp = string, -- Specifies a subdirectory of the plugin to add to runtimepath.
opt = boolean, -- Manually marks a plugin as optional.
bufread = boolean, -- Manually specifying if a plugin needs BufRead after being loaded
branch = string, -- Specifies a git branch to use
tag = string, -- Specifies a git tag to use. Supports '*' for "latest tag"
commit = string, -- Specifies a git commit to use
lock = boolean, -- Skip updating this plugin in updates/syncs. Still cleans.
run = string, function, or table, -- Post-update/install hook. See "update/install hooks".
requires = string or list, -- Specifies plugin dependencies. See "dependencies".
rocks = string or list, -- Specifies Luarocks dependencies for the plugin
config = string or function, -- Specifies code to run after this plugin is loaded.
-- The setup key implies opt = true
setup = string or function, -- Specifies code to run before this plugin is loaded. The code is ran even if
-- the plugin is waiting for other conditions (ft, cond...) to be met.
-- The following keys all imply lazy-loading and imply opt = true
cmd = string or list, -- Specifies commands which load this plugin. Can be an autocmd pattern.
ft = string or list, -- Specifies filetypes which load this plugin.
keys = string or list, -- Specifies maps which load this plugin. See "Keybindings".
event = string or list, -- Specifies autocommand events which load this plugin.
fn = string or list -- Specifies functions which load this plugin.
cond = string, function, or list of strings/functions, -- Specifies a conditional test to load this plugin
module = string or list -- Specifies Lua module names for require. When requiring a string which starts
-- with one of these module names, the plugin will be loaded.
module_pattern = string/list -- Specifies Lua pattern of Lua module names for require. When
-- requiring a string which matches one of these patterns, the plugin will be loaded.
}
*/
// PluginSpec translated to haxe
typedef PluginSpec = {
@idx(1)
final name:String;
final ?disable:Bool;
final ?as:String;
final ?installer:Void -> Void;
final ?updater:Void -> Void;
final ?after:String;
final ?rtp:String;
final ?opt:Bool;
final ?bufread:Bool;
final ?branch:String;
final ?tag:String;
final ?commit:String;
final ?lock:Bool;
final ?run:String;
final ?requires:LuaArray< String >;
final ?rocks:String;
final ?config:Void -> Void;
final ?setup:String;
final ?cmd:String;
final ?ft:String;
final ?keys:String;
final ?event:LuaArray< String >;
final ?fn:String;
final ?cond:String;
final ?module:String;
final ?module_pattern:String;
}

inline extern function packer_plugins():Null< lua.Table< String, {loaded:Bool, path:String, url:String} > >
return untyped __lua__("_G.packer_plugins");

/**
Returns the git commit hash of the plugin.
It only looks for plugins that are part of the packer plugin list.
If the list is not available, it returns "unknown".
*/
function get_plugin_version(name:String):String {
return if (packer_plugins() != null) {
final path = packer_plugins()[cast name].path;
Expand All @@ -23,3 +97,113 @@ function get_plugin_version(name:String):String {
"unknown";
}
}

/**
Checks if packer is installed and installs it if not.
Returns true if packer was not installed and the installation was performed.
You should use this information to decide if you need to run sync()
*/
function ensureInstalled():Bool {
final install_path = Fn.stdpath("data") + "/site/pack/packer/start/packer.nvim";
return if (vim.Fn.empty(vim.Fn.glob(install_path, null)) > 0) {
vim.Fn.system(t([
"git",
"clone",
"--depth",
"1",
"https://github.com/wbthomason/packer.nvim",
install_path
]), null);
Vim.cmd("packadd packer.nvim");
return true;
} else {
return false;
};
}

abstract Plugin(PluginSpec) {
@:from
public static inline function from(spec:PluginSpec):Plugin {
return untyped __lua__(
"{
{0},
disable={1},
as={2},
installer={3},
updater={4},
after={5},
rtp={6},
opt={7},
bufread={8},
branch={9},
tag={10},
commit={11},
lock={12},
run={13},
requires={14},
rocks={15},
config={16},
setup={17},
cmd={18},
ft={19},
keys={20},
event={21},
fn={22},
cond={23},
module={24},
module_pattern={25}
}",
spec.name,
spec.disable,
spec.as,
spec.installer,
spec.updater,
spec.after,
spec.rtp,
spec.opt,
spec.bufread,
spec.branch,
spec.tag,
spec.commit,
spec.lock,
spec.run,
spec.requires,
spec.rocks,
spec.config,
spec.setup,
spec.cmd,
spec.ft,
spec.keys,
spec.event,
spec.fn,
spec.cond,
spec.module,
spec.module_pattern
);
};
}

extern class Packer {
@:luaDotMethod
function startup(use:(Plugin -> Void) -> Void):Void;
@:luaDotMethod
function sync():Void;

/**
Does the packer initlization and returns true if packer was not installed and the installation was performed.
Takes an array of plugin specs to install.
*/
inline static function init(plugins:Array< Plugin >):Bool {
final is_bootstrap = ensureInstalled();
final packer:Packer = lua.Lua.require("packer");
packer.startup((use) -> {
for (plugin in plugins) {
use(plugin);
}
});
if (is_bootstrap) {
packer.sync();
}
return is_bootstrap;
}
}
18 changes: 18 additions & 0 deletions src/vim/Vim.hx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,24 @@ abstract Vector3< A, B, C >(lua.Table< Int, Dynamic >) {
}
}

abstract Vector4< A, B, C, D >(lua.Table< Int, Dynamic >) {
inline public function first():A {
return this[1];
}

inline public function second():B {
return this[2];
}

inline public function third():C {
return this[3];
}

inline public function last():D {
return this[4];
}
}

@:native("vim.spell")
extern class Spell {
public static function check(str:String):Array< Vector3< String, String, Int > >;
Expand Down
Loading

0 comments on commit e995ef3

Please sign in to comment.