Skip to content

Commit ff8c402

Browse files
authored
Merge pull request #1 from matkrin/snippets
Add snippets support
2 parents 9ff60b0 + 90e562c commit ff8c402

4 files changed

Lines changed: 283 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ linting.
5454
- Executables in PATH
5555
- Resolve with `help` output as docs for keywords and builtins
5656
- Resolve with man page as docs for executables
57+
- Snippets
5758

5859
### Document Symbols
5960

internal/lsp/textdocument_completion.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ type CompletionItem struct {
4949
// Preselect
5050
// SortText
5151
// FilterText
52-
// InsertText
53-
// InsertTextFormat
52+
InsertText *string `json:"insertText"`
53+
InsertTextFormat *InsertTextFormat `json:"insertTextFormat"`
5454
// InsertTextMode
5555
// TextEdit
5656
// TextEditText
@@ -106,3 +106,10 @@ const (
106106
MarkupKindPlainText MarkupKind = "plaintext"
107107
MarkupKindMarkdown MarkupKind = "markdown"
108108
)
109+
110+
type InsertTextFormat int
111+
112+
const (
113+
InsertTextFormatPlainText InsertTextFormat = iota + 1
114+
InsertTextFormatSnippet
115+
)

internal/server/completion.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func handleCompletion(request *lsp.CompletionRequest, state *State) *lsp.Complet
3232
completionList = append(completionList, completionKeywords()...)
3333
completionList = append(completionList, completionBuiltins()...)
3434
completionList = append(completionList, completionPathItem(state)...)
35+
completionList = append(completionList, completionSnippets()...)
3536
}
3637

3738
response := lsp.NewCompletionResponse(request.ID, completionList)
@@ -167,3 +168,25 @@ func completionPathItem(state *State) []lsp.CompletionItem {
167168
}
168169
return result
169170
}
171+
172+
// Completion for snippets
173+
func completionSnippets() []lsp.CompletionItem {
174+
var result []lsp.CompletionItem
175+
176+
insertTextFormat := lsp.InsertTextFormatSnippet
177+
for _, snippet := range SNIPPETS {
178+
result = append(result, lsp.CompletionItem{
179+
Label: snippet.label,
180+
Kind: lsp.CompletionSnippet,
181+
Detail: "",
182+
Documentation: &lsp.MarkupContent{
183+
Kind: lsp.MarkupKindMarkdown,
184+
Value: snippet.documentation,
185+
},
186+
InsertText: &snippet.insertText,
187+
InsertTextFormat: &insertTextFormat,
188+
})
189+
}
190+
191+
return result
192+
}

internal/server/snippets.go

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package server
2+
3+
var SNIPPETS = []struct {
4+
label string
5+
documentation string
6+
insertText string
7+
}{
8+
{
9+
"for",
10+
"for-in loop",
11+
"for ${1:ITEM} in ${2:ITEMS}; do\n\t${0::}\ndone",
12+
},
13+
{
14+
"for",
15+
"C-style foor loop",
16+
"for (( ${1:i} = 0; ${1:i} < ${2:n}; ${1:i}++ )); do\n\t${0::}\ndone",
17+
},
18+
{
19+
"for in directory",
20+
"foor loop over files in a directory",
21+
"for ${1:FILE} in \"${2:DIR}\"/*; do\n\t${0::}\ndone",
22+
},
23+
{
24+
"while",
25+
"while loop",
26+
"while ${1:PREDICATE}; do\n\t${0::}\ndone",
27+
},
28+
{
29+
"while read file",
30+
"while loop over lines in a file",
31+
"while IFS= read -r ${1:LINE}; do\n\t${0::}\ndone < \"${2:FILE}\"",
32+
},
33+
{
34+
"while getopts",
35+
"while loop with getopts command",
36+
`while getopts "${1:ab:c:}" opt; do
37+
case "\$opt" in
38+
a) ;;
39+
b) ${2:VALUE}="\$OPTARG" ;;
40+
c) ${3:VALUE}="\$OPTARG" ;;
41+
*) exit 1 ;;
42+
esac
43+
done`,
44+
},
45+
{
46+
"if",
47+
"if statement",
48+
"if [[ ${1:PREDICATE} ]]; then\n\t${0::}\nfi",
49+
},
50+
{
51+
"if else",
52+
"if else statement",
53+
"if [[ ${1:PREDICATE} ]]; then\n\t${2::}\nelse\n\t${0::}\nfi",
54+
},
55+
{
56+
"if file exists",
57+
"if statement that checks existance of a file",
58+
"if [[ -f \"${1:FILE}\" ]]; then\n\t${0::}\nfi",
59+
},
60+
{
61+
"if directory exists",
62+
"if statement that checks existance of a directory",
63+
"if [[ -d \"${1:DIRECTORY}\" ]]; then\n\t${0::}\nfi",
64+
},
65+
{
66+
"if string non-zero",
67+
"if statement that checks if a string has non-zero length",
68+
"if [[ -n \"${1:VAR}\" ]]; then\n\t${0::}\nfi",
69+
},
70+
{
71+
"if string zero",
72+
"if statement that checks if a string has zero length",
73+
"if [[ -z \"${1:VAR}\" ]]; then\n\t${0::}\nfi",
74+
},
75+
{
76+
"if command exists",
77+
"if statement that checks if executable exists in PATH",
78+
"if command -v ${1:EXECUTABLE} >/dev/null 2>&1; then\n\t${0::}\nfi",
79+
},
80+
{
81+
"case",
82+
"case statement",
83+
"case \"${1:VALUE}\" in\n${2:PATTERN})\n\t${0::}\n\t;;\n*)\n\t;;\nesac",
84+
},
85+
{
86+
"start",
87+
"sensible preamble",
88+
"#!/usr/bin/env bash\n\nset -euo pipefail\n\n${0}",
89+
},
90+
{
91+
"declare array",
92+
"declare and array with values",
93+
"declare -a ${1:ARR}=(\n\t\"${2:ITEM}\"\n)\n$0",
94+
},
95+
{
96+
"declare associative array",
97+
"declare an associative array with keys and values",
98+
"declare -A ${1:MAP}=(\n\t[\"${2:KEY}\"]=\"${3:VALUE}\"\n)\n$0",
99+
},
100+
{
101+
"read -r",
102+
"read with -r (no backslash escape)",
103+
"read -r ${1:VAR}",
104+
},
105+
{
106+
"read -rp",
107+
"read with -r (no backslash escape) and -p (prompt)",
108+
"read -rp \"${1:PROMPT: }\" ${2:VAR}",
109+
},
110+
{
111+
"read comma list",
112+
"read a comma separated input into array",
113+
"IFS=',' read -ra ${1:PARTS} <<< \"${2:INPUT}\"",
114+
},
115+
{
116+
"printf",
117+
"",
118+
"printf '%s\n' \"${1:VALUE}\"",
119+
},
120+
{
121+
"echo stderr",
122+
"echo to stderr",
123+
"echo >&2 \"${1:ERROR_MSG}\"",
124+
},
125+
{
126+
"heredoc",
127+
"",
128+
"cat <<EOF\n$0\nEOF",
129+
},
130+
{
131+
"heredoc to file",
132+
"",
133+
"cat > \"${1:FILE}\" <<EOF\n$0\nEOF",
134+
},
135+
{
136+
"trap EXIT",
137+
"",
138+
"trap '${1:CLEANUP}' EXIT",
139+
},
140+
{
141+
"trap INT TERM",
142+
"",
143+
"trap '${1:HANDLER}' INT TERM",
144+
},
145+
{
146+
"cleanup",
147+
"cleanup function",
148+
"cleanup() {\n\t${0::}\n}\n\ntrap cleanup EXIT",
149+
},
150+
{
151+
"tmpdir",
152+
"",
153+
"tmpdir=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmpdir\"' EXIT\n$0",
154+
},
155+
{
156+
"command-substitution",
157+
"",
158+
"\\$( ${0:COMMAND} )",
159+
},
160+
{
161+
"process-substitution",
162+
"",
163+
"<( ${0:COMMAND} )",
164+
},
165+
{
166+
"var default",
167+
"parameter expansion with default value",
168+
"\"\\${${1:VAR:-${2:DEFAULT}}}\"",
169+
},
170+
{
171+
"var-error",
172+
"parameter expansion with check for null or unset",
173+
"\"\\${${1:VAR:?${2:ERROR_MSG}}}\"",
174+
},
175+
{
176+
"find file",
177+
"find a file with given extension",
178+
"find \"${1:PATH}\" -type f -name \"${2:*.EXT}\"",
179+
},
180+
{
181+
"mapfile lines",
182+
"mapfile that creates a array over all lines in a file",
183+
"mapfile -t ${1:LINES} < \"${2:FILE}\"",
184+
},
185+
{
186+
"scriptdir",
187+
"gets the directory where scripti is located",
188+
"script_dir=\"\\$(cd -- \"\\$(dirname -- \"\\${BASH_SOURCE[0]}\")\" && pwd)\"",
189+
},
190+
{
191+
"date",
192+
"",
193+
"date +'%Y-%m-%d'",
194+
},
195+
{
196+
"date ISO 8601",
197+
"",
198+
"date +'%Y-%m-%dT%H:%M:%S%z'",
199+
},
200+
{
201+
"install",
202+
"",
203+
"install -Dm755 \"${1:SRC}\" \"${2:DST}\"",
204+
},
205+
{
206+
"usage",
207+
"usage function",
208+
"usage() {\n\tcat <<EOF\nUsage: ${1:script} [options]\n\nEOF\n }",
209+
},
210+
{
211+
"help",
212+
"help function",
213+
`print-help() {
214+
cat <<EOF
215+
${1:PROGRAM_NAME} - ${2:SHORT_DESCRIPTION} (version ${3:VERSION})
216+
217+
Usage: ${1:PROGRAM_NAME} [OPTIONS] <VALUE>
218+
219+
Arguments:
220+
VALUE An argument value
221+
222+
Options:
223+
-o, --optional <VALUE> Provide an optional argument
224+
-f, --flag Set a flag
225+
-h, --help Print help
226+
-V, --version Print version
227+
EOF
228+
}`,
229+
},
230+
{
231+
"python command",
232+
"python command with heredoc",
233+
"python3 - <<'PY'\n${0:PYTHON}\nPY",
234+
},
235+
{
236+
"log",
237+
"log function",
238+
`log() { printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }`,
239+
},
240+
{
241+
"die",
242+
"die function",
243+
`die() { echo >&2 "$*"; exit 1; }`,
244+
},
245+
{
246+
"require",
247+
"require function",
248+
`require() { command -v "$1" >/dev/null 2>&1 || die "missing dependency: $1"; }`,
249+
},
250+
}

0 commit comments

Comments
 (0)