zardoy / vscode-better-snippets

Create most advanced snippets for VSCode in existence
https://marketplace.visualstudio.com/items?itemName=zardoy.better-snippets
MIT License
6 stars 0 forks source link

feature: Add ability to load snippets from file or directory #4

Open iilyak opened 2 years ago

iilyak commented 2 years ago

The https://github.com/asbjornh/vscode-module-templates extension supports loading templates from files using separate configuration field module-templates.templateFiles.

This extension could use similar approach as well. For example

"betterSnippets.customSnippets": [
        {
            "name": "foo",
            "snippetFile"

However ideally the configuration like follows would be perfect for my use case.

"betterSnippets.snippetsDir": [
    "./snippets"
]

cat ./snippets/cdb-debug.erl

---
name: cdb-debug
when:
  languages:
  - erlang
  locations:
  - fileStart
---
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
%   http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.

-module(${TM_FILENAME_BASE}).

-export([
    help/0,
    help/1
]).

-export([
]).

help() ->
    [
        %% list of provided commands
        ${1:opened_files}
    ].

-spec help(Function :: atom()) -> ok.
%% erlfmt-ignore
help(${1:opened_files}) ->
    io:format("
    ${1:opened_files}
    --------------

    Returns ${2:[]}
    ---
    ", []);
help(Unknown) ->
    io:format("Unknown function: `~p`. Please try one of the following:~n", [Unknown]),
    [io:format("    - ~s~n", [Function]) || Function <- help()],
    io:format("    ---~n", []),
    ok.

-spec ${1:opened_files}() ->
    ${3:[]}.

${1:opened_files}() ->
    ${4:[]}.

As you can see all configuration is contained in the frontmatter of the snippet file.

iilyak commented 2 years ago

JFYI the frontmatter based approach is implemented in Foam extension for markdown templates https://foambubble.github.io/foam/features/note-templates.

zardoy commented 2 years ago

Hey! Thank you so much for reporting this issue!

First of all, it would be possible soon to define snippets at workspace level (in .vscode/settings.json).

However, your idea would be extremely useful in cases when snippets are large or you want to track them in individual files.

Do we have some cross-IDE standard? Any extensions from other IDEs that implements something like this?


I think to implement this setting with the following default: ['.better-snippets'] (just like foam do). Ideas about default folder name are welcome!

Also about frontmatter. It is okay to use it in Markdown files, however, for other languages it would be much better to define metadata in comments at the start of file. For example we can have file .better-snippets/cdb-debug.erl:

% .snippet
% locations: fileStart

% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% ...

Empty line is required as it used to denote end of metadata.

Also we can skip definining name and languages as they both can be derived from the file name.

It is much better to define metadata in comments, so we don't break:

  1. Syntax highlighting (especially important for github.com and other IDEs)
  2. Language server type-checking (e.g. important for TypeScript files)

Also, you can expect web support!

zardoy commented 2 years ago
"betterSnippets.customSnippets": [
    {
        "name": "foo",
        "snippetFile": "..."

This case won't be supported as this would make sense only in workspace-level configuration.

In case above and in betterSnippets.snippetsDir paths should not be absolute as in this case you lose:

That's why global snippets should always be defined in VSCode's settings.json. I'll make a sidebar panel for convenient snippet editing (each snippet you could edit in individual tab).

iilyak commented 2 years ago

Do we have some cross-IDE standard? Any extensions from other IDEs that implements something like this?

I am not aware of cross-IDE standards.

Also about frontmatter. It is okay to use it in Markdown files, however, for other languages it would be much better to define metadata in comments at the start of file.

I thought the better-snippets extension would read a file. Split the file in two parts: metadata from frontmatter and snippet body. It would use snippet body and inject it into the currently opened file. The expanded result wouldn't have frontmatter and therefore language independent.

However I do agree the syntax highlighting would be broken.

iilyak commented 2 years ago

That's why global snippets should always be defined in VSCode's settings.json. I'll make a sidebar panel for convenient snippet editing (each snippet you could edit in individual tab).

Would it be possible to have a project specific snippets? For example by checking existence of .vscode/.better_snippets directory? It would be nice if additional directories could be configured:

cat .vscode/settings.json
{
    "betterSnippets.snippetsDirs": [
        "../snippets/erlang",
        "../snippets/elixir",
        "../snippets/docs"
   ]
}

By default the "betterSnippets.snippetsDirs" would be ["./.better-snippets"].

zardoy commented 2 years ago

However I do agree the syntax highlighting would be broken.

Yes, that's why metadata will live in comments for now. As you said the file split in two parts: metadata and body.

Metadata is commented. Body goes as is, but with snippet variables support. Emptyline will be delimiter. Look at example with .better-snippets/cdb-debug.erl from above (https://github.com/zardoy/vscode-better-snippets/issues/4#issuecomment-1043115113)

Would it be possible to have a project specific snippets?

Yes, it would be supported with this setting.

By default the "betterSnippets.snippetsDirs" would be ["./.better-snippets"].

Great! This will be configurable!

iilyak commented 2 years ago

Yes, that's why metadata will live in comments for now. As you said the file split in two parts: metadata and body.

There is one problem with using comments. Most IDE use shebang presence to figure out which syntax to enable if file extension is not reliable. Which is the case for shell scripts. The shebang should be in the first line for this detection to work.

zardoy commented 2 years ago

Sorry for delayed response. I just started implementing this feature.

The shebang should be in the first line for this detection to work.

I see only two solutions:

#!/usr/bin/env bash

# .snippet
# locations: fileStart

if [[ "$OSTYPE" == "darwin"* ]]; then
# ...

The body of the snippet would look like:

#!/usr/bin/env bash

if [[ "$OSTYPE" == "darwin"* ]]; then
# ...

However, I don't like the ambiguity it introduces. For example snippet can have > 100 lines and since snippet metadata can be placed at any line, programmer can spend some time to find it. It can be just annoying.


-module(${TM_FILENAME_BASE}). ...


And second file `.better-snippets/cdb-debug.snippet.json`:
```json
{
    "locations": ["fileStart"]
}

Supported formats would be: json, yaml, js. The last one would allow to run aribtrary code and will be disabled in trusted workspaces. I'm also thinking about support for toml and ts formats.

I more like the second solution and I'm currently planning to implement only it. WDYT?

iilyak commented 2 years ago

second file .better-snippets/cdb-debug.snippet.json would work for my use case.