Skip to content

Commit 577ab7b

Browse files
authored
Merge pull request #37 from x1unix/dev
1.6.0
2 parents d3d28d4 + f0cbda2 commit 577ab7b

File tree

9 files changed

+163
-62
lines changed

9 files changed

+163
-62
lines changed

pkg/analyzer/completion_item.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ const (
3535
Snippet
3636
)
3737

38+
// CompletionItemInsertTextRule is insert text insert rule.
39+
type CompletionItemInsertTextRule int
40+
41+
const (
42+
// KeepWhitespace is adjust whitespace/indentation of
43+
// multiline insert texts to match the current line indentation.
44+
KeepWhitespace CompletionItemInsertTextRule = 1
45+
46+
// InsertAsSnippet means that `insertText` is a snippet.
47+
InsertAsSnippet CompletionItemInsertTextRule = 4
48+
)
49+
3850
// CompletionItem is monaco-editor binding
3951
type CompletionItem struct {
4052
// Label is item label
@@ -47,6 +59,9 @@ type CompletionItem struct {
4759
Documentation interface{} `json:"documentation"`
4860
// InsertText is text to be inserted
4961
InsertText string `json:"insertText"`
62+
// InsertTextRules is a string or snippet that should be inserted in a document
63+
// when selecting this completion. When `falsy` the label in used.
64+
InsertTextRules CompletionItemInsertTextRule `json:"insertTextRules,omitempty"`
5065
}
5166

5267
// MarkdownString is monaco-editor string with markdown

pkg/analyzer/decl.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package analyzer
22

33
import (
44
"go/ast"
5+
"strconv"
56
"strings"
67
)
78

@@ -77,14 +78,52 @@ func funcToString(fn *ast.FuncType) string {
7778
return str
7879
}
7980

81+
// formatFuncInsertText returns `insertText` snippet template for monaco-editor
82+
// from function signature.
83+
//
84+
// Example:
85+
// func sum(a, b int) int
86+
// Will output:
87+
// "sum(${1:a}, ${2:b})"
88+
func formatFuncInsertText(fn *ast.FuncDecl) string {
89+
sb := strings.Builder{}
90+
sb.WriteString(fn.Name.String())
91+
sb.WriteByte('(')
92+
93+
if fn.Type.Params != nil && fn.Type.Params.List != nil {
94+
pos := 1 // in monaco, first input argument should start with 0
95+
params := make([]string, 0, fn.Type.Params.NumFields())
96+
97+
for _, param := range fn.Type.Params.List {
98+
if len(param.Names) == 0 {
99+
// $pos
100+
params = append(params, "$"+strconv.Itoa(pos))
101+
pos++
102+
continue
103+
}
104+
105+
for _, p := range param.Names {
106+
// ${pos:paramName}
107+
params = append(params, "${"+strconv.Itoa(pos)+":"+p.Name+"}")
108+
pos++
109+
}
110+
}
111+
sb.WriteString(strings.Join(params, ", "))
112+
}
113+
114+
sb.WriteRune(')')
115+
return sb.String()
116+
}
117+
80118
func funcToItem(fn *ast.FuncDecl) *CompletionItem {
81119
ci := &CompletionItem{
82-
Label: fn.Name.String(),
83-
Kind: Function,
84-
Documentation: formatDoc(fn.Doc.Text()),
120+
Label: fn.Name.String(),
121+
Kind: Function,
122+
Detail: funcToString(fn.Type),
123+
Documentation: formatDoc(fn.Doc.Text()),
124+
InsertText: formatFuncInsertText(fn),
125+
InsertTextRules: InsertAsSnippet,
85126
}
86127

87-
ci.Detail = funcToString(fn.Type)
88-
ci.InsertText = ci.Label + "()"
89128
return ci
90129
}

pkg/analyzer/scanner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (p *PackageScanner) appendFunc(fn *ast.FuncDecl, dest *SymbolIndex) {
7878
}
7979

8080
item := funcToItem(fn)
81-
log.Debugf("found function '%s.%s'", p.name, item.InsertText)
81+
log.Debugf("found function '%s.%s'", p.name, item.Detail)
8282
dest.Append(item)
8383
}
8484

pkg/analyzer/scanner_test.go

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,63 @@ import (
1212
var examplePackageSummary = PackageSummary{
1313
Functions: newSymbolIndex([]*CompletionItem{
1414
{
15-
Label: "SomeFunc",
16-
Kind: Function,
17-
Detail: "func(val string) string",
18-
InsertText: "SomeFunc()",
15+
Label: "SomeFunc",
16+
Kind: Function,
17+
Detail: "func(val string) string",
18+
InsertText: "SomeFunc(${1:val})",
19+
InsertTextRules: InsertAsSnippet,
1920
Documentation: NewMarkdownString(
2021
"SomeFunc is test function sample\nwith doc that contains code sample:" +
2122
"\n\n```\n\ta := \"foo\"\n\tfmt.PrintLn(a)\n\n```\nend\n\n",
2223
),
2324
},
2425
{
25-
Label: "ChanArrFunc",
26-
Kind: Function,
27-
Detail: "func(items ...string) chan string",
28-
InsertText: "ChanArrFunc()",
29-
Documentation: NewMarkdownString("ChanArrFunc is stub\n\n"),
26+
Label: "ChanArrFunc",
27+
Kind: Function,
28+
Detail: "func(items ...string) chan string",
29+
InsertText: "ChanArrFunc(${1:items})",
30+
InsertTextRules: InsertAsSnippet,
31+
Documentation: NewMarkdownString("ChanArrFunc is stub\n\n"),
3032
},
3133
{
32-
Label: "SomeFunc2",
33-
Kind: Function,
34-
Detail: "func(m map[string]interface{}, v *int) []interface{}",
35-
InsertText: "SomeFunc2()",
36-
Documentation: NewMarkdownString("SomeFunc2 is func stub\n\n"),
34+
Label: "SomeFunc2",
35+
Kind: Function,
36+
Detail: "func(m map[string]interface{}, v *int) []interface{}",
37+
InsertText: "SomeFunc2(${1:m}, ${2:v})",
38+
InsertTextRules: InsertAsSnippet,
39+
Documentation: NewMarkdownString("SomeFunc2 is func stub\n\n"),
3740
},
3841
{
39-
Label: "IfaceFunc",
40-
Kind: Function,
41-
Detail: "func() Action",
42-
InsertText: "IfaceFunc()",
43-
Documentation: NewMarkdownString("IfaceFunc is stub with unterminated code block\n```\n\t2 + 2\n\n```\n"),
42+
Label: "IfaceFunc",
43+
Kind: Function,
44+
Detail: "func() Action",
45+
InsertText: "IfaceFunc()",
46+
InsertTextRules: InsertAsSnippet,
47+
Documentation: NewMarkdownString("IfaceFunc is stub with unterminated code block\n```\n\t2 + 2\n\n```\n"),
4448
},
4549
{
46-
Label: "FuncReturnFuncAndIface",
47-
Kind: Function,
48-
Detail: "func() (func() (string, error), interface{\nf func()\n})",
49-
InsertText: "FuncReturnFuncAndIface()",
50-
Documentation: NewMarkdownString("FuncReturnFuncAndIface is stub\n\n"),
50+
Label: "FuncReturnFuncAndIface",
51+
Kind: Function,
52+
Detail: "func() (func() (string, error), interface{\nf func()\n})",
53+
InsertText: "FuncReturnFuncAndIface()",
54+
InsertTextRules: InsertAsSnippet,
55+
Documentation: NewMarkdownString("FuncReturnFuncAndIface is stub\n\n"),
56+
},
57+
{
58+
Label: "XXX",
59+
Kind: Function,
60+
Detail: "func(a string, b string)",
61+
InsertText: "XXX(${1:a}, ${2:b})",
62+
InsertTextRules: InsertAsSnippet,
63+
Documentation: NewMarkdownString("XXX is function example\n\n"),
64+
},
65+
{
66+
Label: "FuncUnnamedParams",
67+
Kind: Function,
68+
Detail: "func(string)",
69+
InsertText: "FuncUnnamedParams($1)",
70+
InsertTextRules: InsertAsSnippet,
71+
Documentation: NewMarkdownString("FuncUnnamedParams is function with unnamed params\n\n"),
5172
},
5273
}),
5374
Values: newSymbolIndex([]*CompletionItem{

pkg/analyzer/testdata/src/example/example.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,11 @@ func IfaceFunc() Action {
7777
func FuncReturnFuncAndIface() (func() (string, error), interface{ f() }) {
7878
return nil, nil
7979
}
80+
81+
// XXX is function example
82+
func XXX(a, b string) {
83+
84+
}
85+
86+
// FuncUnnamedParams is function with unnamed params
87+
func FuncUnnamedParams(string) {}

web/src/ChangeLogModal.tsx

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,14 @@ export default function ChangeLogModal(props: ChangeLogModalProps) {
3939
<p>
4040
<b>Interface - Global</b>
4141
<ul>
42-
<li>Added list of snippets with <u>templates and tutorials</u> near <b>Open</b> menu item</li>
43-
<li>Moved <b>Settings</b> menu button from drop-down to main section</li>
44-
</ul>
45-
</p>
46-
<p>
47-
<b>Interface - Settings</b>
48-
<ul>
49-
<li>Added editor fonts selector</li>
50-
<li>Added support of font code ligatures</li>
51-
<li>Fixed fallback font issue that might cause issues on Linux</li>
42+
<li>Add <kbd>F5</kbd> hotkey for <b>Run</b> action</li>
5243
</ul>
5344
</p>
5445
<p>
5546
<b>Interface - Editor</b>
5647
<ul>
57-
<li>
58-
Added code snippets to make code input faster:
59-
<ul>
60-
<li><code>iferr</code> - Error handling snippet</li>
61-
<li><code>switch</code> - Quick switch declaration</li>
62-
<li><code>typestruct</code> - Quickly declare struct</li>
63-
<li><code>fmtprintf</code> - fmt.Printf shorthand</li>
64-
<li>and other (see release notes)</li>
65-
</ul>
66-
</li>
48+
<li>Suggest function arguments completion</li>
49+
<li>Fix snippets suggestion issue</li>
6750
</ul>
6851
</p>
6952
<p>

web/src/Header.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ export class Header extends React.Component<any, HeaderState> {
4646
};
4747
}
4848

49+
onKeyCommand = (e: KeyboardEvent) => {
50+
switch (e.code) {
51+
case 'F5':
52+
e.preventDefault();
53+
this.props.dispatch(runFileDispatcher);
54+
return;
55+
default:
56+
return;
57+
}
58+
}
59+
4960
componentDidMount(): void {
5061
const fileElement = document.createElement('input') as HTMLInputElement;
5162
fileElement.type = 'file';
@@ -59,6 +70,13 @@ export class Header extends React.Component<any, HeaderState> {
5970
if (!version) return;
6071
this.setState({showUpdateBanner: version !== config.appVersion});
6172
}).catch(err => console.warn('failed to check server API version: ', err));
73+
74+
// register keybindings
75+
window.addEventListener('keydown', this.onKeyCommand)
76+
}
77+
78+
componentWillUnmount() {
79+
window.removeEventListener('keydown', this.onKeyCommand);
6280
}
6381

6482
onItemSelect() {
@@ -91,7 +109,8 @@ export class Header extends React.Component<any, HeaderState> {
91109
{
92110
key: 'run',
93111
text: 'Run',
94-
ariaLabel: 'Run',
112+
ariaLabel: 'Run program (F5)',
113+
title: 'Run program (F5)',
95114
iconProps: {iconName: 'Play'},
96115
disabled: this.props.loading,
97116
onClick: () => {

web/src/editor/provider.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,34 @@ class GoCompletionItemProvider implements monaco.languages.CompletionItemProvide
5252
return Promise.resolve({suggestions: []});
5353
}
5454

55+
let word = model.getWordUntilPosition(position);
56+
let range = {
57+
startLineNumber: position.lineNumber,
58+
endLineNumber: position.lineNumber,
59+
startColumn: word.startColumn,
60+
endColumn: word.endColumn
61+
};
62+
63+
// filter snippets by prefix.
64+
// usually monaco does that but not always in right way
65+
const relatedSnippets = snippets
66+
.filter(s => s.label.toString().startsWith(query.value))
67+
.map(s => ({...s, range}));
68+
5569
try {
56-
const resp = await this.client.getSuggestions(query);
57-
resp.suggestions = resp.suggestions ? snippets.concat(resp.suggestions) : snippets;
58-
return resp;
70+
const {suggestions} = await this.client.getSuggestions(query);
71+
if (!suggestions) {
72+
return {
73+
suggestions: relatedSnippets
74+
}
75+
}
76+
77+
return {
78+
suggestions: relatedSnippets.concat(suggestions.map(s => ({...s, range})))
79+
}
5980
} catch (err) {
6081
console.error(`Failed to get code completion from server: ${err.message}`);
61-
return {suggestions: snippets};
82+
return {suggestions: relatedSnippets};
6283
}
6384
}
6485
}

web/src/editor/snippets.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import * as monaco from 'monaco-editor';
22

33
/* eslint-disable no-template-curly-in-string */
44

5-
// Import aliases
6-
type CompletionList = monaco.languages.CompletionList;
7-
type CompletionContext = monaco.languages.CompletionContext;
8-
type ITextModel = monaco.editor.ITextModel;
9-
type Position = monaco.Position;
10-
5+
const Rule = monaco.languages.CompletionItemInsertTextRule;
116
/**
127
* List of snippets for editor
138
*/
@@ -121,7 +116,7 @@ const snippets = [
121116

122117
].map(s => ({
123118
kind: monaco.languages.CompletionItemKind.Snippet,
124-
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
119+
insertTextRules: Rule.InsertAsSnippet,
125120
...s,
126121
}));
127122

0 commit comments

Comments
 (0)