Skip to content
This repository has been archived by the owner on Mar 1, 2022. It is now read-only.

Commit

Permalink
Add support for dependency based snippets
Browse files Browse the repository at this point in the history
  • Loading branch information
WebFreak001 committed Dec 9, 2019
1 parent b463228 commit 4606d2b
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 2 deletions.
131 changes: 131 additions & 0 deletions source/workspaced/com/snippets/dependencies.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
module workspaced.com.snippets.dependencies;

import workspaced.api;
import workspaced.com.dub;
import workspaced.com.snippets;

import std.algorithm;

///
alias SnippetList = PlainSnippet[];

/// A list of dependencies usable in an associative array
struct DependencySet
{
string[] sorted;

void set(string[] deps)
{
deps.sort!"a<b";
sorted = deps;
}

bool hasAll(string[] deps) const
{
deps.sort!"a<b";
int a, b;
while (a < sorted.length && b < deps.length)
{
const as = sorted[a];
const bs = deps[b];
const c = cmp(as, bs);

if (c == 0)
{
a++;
b++;
}
else if (c < 0)
return false;
else
b++;
}
return a == sorted.length;
}

bool opEquals(const ref DependencySet other) const
{
return sorted == other.sorted;
}

size_t toHash() const @safe nothrow
{
size_t ret;
foreach (v; sorted)
ret ^= typeid(v).getHash((() @trusted => &v)());
return ret;
}
}

class DependencyBasedSnippetProvider : SnippetProvider
{
SnippetList[DependencySet] snippets;

void addSnippet(string[] requiredDependencies, PlainSnippet snippet)
{
DependencySet set;
set.set(requiredDependencies);

if (auto v = set in snippets)
*v ~= snippet;
else
snippets[set] = [snippet];
}

Future!(Snippet[]) provideSnippets(scope const WorkspaceD.Instance instance,
scope const(char)[] file, scope const(char)[] code, int position, const SnippetInfo info)
{
if (!instance.has!DubComponent)
return Future!(Snippet[]).fromResult(null);
else
{
string id = typeid(this).name;
auto dub = instance.get!DubComponent;
return Future!(Snippet[]).async(delegate() {
string[] deps;
foreach (dep; dub.dependencies)
{
deps ~= dep.name;
deps ~= dep.dependencies.keys;
}
Snippet[] ret;
foreach (k, v; snippets)
{
if (k.hasAll(deps))
{
foreach (snip; v)
if (snip.levels.canFind(info.level))
ret ~= snip.buildSnippet(id);
}
}
return ret;
});
}
}

Future!Snippet resolveSnippet(scope const WorkspaceD.Instance instance,
scope const(char)[] file, scope const(char)[] code, int position,
const SnippetInfo info, Snippet snippet)
{
snippet.resolved = true;
return Future!Snippet.fromResult(snippet);
}
}

unittest
{
DependencySet set;
set.set(["vibe-d", "mir", "serve-d"]);
assert(set.hasAll(["vibe-d", "serve-d", "mir"]));
assert(set.hasAll(["vibe-d", "serve-d", "serve-d", "serve-d", "mir", "mir"]));
assert(set.hasAll(["vibe-d", "serve-d", "mir", "workspace-d"]));
assert(set.hasAll(["diet-ng", "vibe-d", "serve-d", "mir", "workspace-d"]));
assert(!set.hasAll(["diet-ng", "serve-d", "mir", "workspace-d"]));
assert(!set.hasAll(["diet-ng", "serve-d", "vibe-d", "workspace-d"]));
assert(!set.hasAll(["diet-ng", "mir", "mir", "vibe-d", "workspace-d"]));
assert(!set.hasAll(["diet-ng", "mir", "vibe-d", "workspace-d"]));

set.set(null);
assert(set.hasAll([]));
assert(set.hasAll(["foo"]));
}
21 changes: 19 additions & 2 deletions source/workspaced/com/snippets/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,27 @@ import std.string;

public import workspaced.com.snippets.plain;
public import workspaced.com.snippets.smart;
public import workspaced.com.snippets.dependencies;

/// Component for auto completing snippets with context information and formatting these snippets with dfmt.
@component("snippets")
class SnippetsComponent : ComponentWrapper
{
mixin DefaultComponentWrapper;

static PlainSnippetProvider plainSnippets;
static SmartSnippetProvider smartSnippets;
static DependencyBasedSnippetProvider dependencySnippets;

protected SnippetProvider[] providers;

protected void load()
{
config.stringBehavior = StringBehavior.source;
providers.reserve(16);
providers ~= new PlainSnippetProvider();
providers ~= new SmartSnippetProvider();
providers ~= plainSnippets = new PlainSnippetProvider();
providers ~= smartSnippets = new SmartSnippetProvider();
providers ~= dependencySnippets = new DependencyBasedSnippetProvider();
}

/**
Expand Down Expand Up @@ -286,6 +292,17 @@ class SnippetsComponent : ComponentWrapper
return res;
}

/// Adds snippets which complete conditionally based on dub dependencies being present.
/// This function affects the global configuration of all instances.
/// Params:
/// requiredDependencies = The dependencies which must be present in order for this snippet to show up.
/// snippet = The snippet to suggest when the required dependencies are matched.
void addDependencySnippet(string[] requiredDependencies, PlainSnippet snippet)
{
// maybe application global change isn't such a good idea? Current config system seems too inefficient for this.
dependencySnippets.addSnippet(requiredDependencies, snippet);
}

private:
Future!(Snippet[])[] collectSnippets(scope const(char)[] file,
scope const(char)[] code, int position)
Expand Down

0 comments on commit 4606d2b

Please sign in to comment.