private runGoCode()

in src/goSuggest.ts [279:491]


	private runGoCode(
		document: vscode.TextDocument,
		filename: string,
		inputText: string,
		offset: number,
		inString: boolean,
		position: vscode.Position,
		lineText: string,
		currentWord: string,
		includeUnimportedPkgs: boolean,
		config: vscode.WorkspaceConfiguration
	): Thenable<vscode.CompletionItem[]> {
		return new Promise<vscode.CompletionItem[]>((resolve, reject) => {
			const gocodeName = this.isGoMod ? 'gocode-gomod' : 'gocode';
			const gocode = getBinPath(gocodeName);
			if (!path.isAbsolute(gocode)) {
				promptForMissingTool(gocodeName);
				return reject();
			}

			const env = toolExecutionEnvironment();
			let stdout = '';
			let stderr = '';

			// stamblerre/gocode does not support -unimported-packages flags.
			if (this.isGoMod) {
				const unimportedPkgIndex = this.gocodeFlags.indexOf('-unimported-packages');
				if (unimportedPkgIndex >= 0) {
					this.gocodeFlags.splice(unimportedPkgIndex, 1);
				}
			}

			// -exclude-docs is something we use internally and is not related to gocode
			const excludeDocsIndex = this.gocodeFlags.indexOf('-exclude-docs');
			if (excludeDocsIndex >= 0) {
				this.gocodeFlags.splice(excludeDocsIndex, 1);
				this.excludeDocs = true;
			}

			// Spawn `gocode` process
			const p = cp.spawn(gocode, [...this.gocodeFlags, 'autocomplete', filename, '' + offset], { env });
			p.stdout.on('data', (data) => (stdout += data));
			p.stderr.on('data', (data) => (stderr += data));
			p.on('error', (err) => {
				if (err && (<any>err).code === 'ENOENT') {
					promptForMissingTool(gocodeName);
					return reject();
				}
				return reject(err);
			});
			p.on('close', (code) => {
				try {
					if (code !== 0) {
						if (stderr.indexOf("rpc: can't find service Server.AutoComplete") > -1 && !this.killMsgShown) {
							vscode.window.showErrorMessage(
								'Auto-completion feature failed as an older gocode process is still running. Please kill the running process for gocode and try again.'
							);
							this.killMsgShown = true;
						}
						if (stderr.startsWith('flag provided but not defined:')) {
							promptForUpdatingTool(gocodeName);
						}
						return reject();
					}
					const results = <[number, GoCodeSuggestion[]]>JSON.parse(stdout.toString());
					let suggestions: vscode.CompletionItem[] = [];
					const packageSuggestions: string[] = [];

					const wordAtPosition = document.getWordRangeAtPosition(position);
					let areCompletionsForPackageSymbols = false;
					if (results && results[1]) {
						for (const suggest of results[1]) {
							if (inString && suggest.class !== 'import') {
								continue;
							}
							const item = new ExtendedCompletionItem(suggest.name);
							item.kind = vscodeKindFromGoCodeClass(suggest.class, suggest.type);
							item.package = suggest.package;
							item.receiver = suggest.receiver;
							item.fileName = document.fileName;
							item.detail = suggest.type;
							if (!areCompletionsForPackageSymbols && item.package && item.package !== 'builtin') {
								areCompletionsForPackageSymbols = true;
							}
							if (suggest.class === 'package') {
								let { label } = item;
								if (typeof label !== 'string') label = label.label;
								const possiblePackageImportPaths = this.getPackageImportPath(label);
								if (possiblePackageImportPaths.length === 1) {
									item.detail = possiblePackageImportPaths[0];
								}
								packageSuggestions.push(suggest.name);
							}
							if (inString && suggest.class === 'import') {
								item.textEdit = new vscode.TextEdit(
									new vscode.Range(
										position.line,
										lineText.substring(0, position.character).lastIndexOf('"') + 1,
										position.line,
										position.character
									),
									suggest.name
								);
							}
							if (
								(config['useCodeSnippetsOnFunctionSuggest'] ||
									config['useCodeSnippetsOnFunctionSuggestWithoutType']) &&
								((suggest.class === 'func' && lineText.substr(position.character, 2) !== '()') || // Avoids met() -> method()()
									(suggest.class === 'var' &&
										suggest.type.startsWith('func(') &&
										lineText.substr(position.character, 1) !== ')' && // Avoids snippets when typing params in a func call
										lineText.substr(position.character, 1) !== ',')) // Avoids snippets when typing params in a func call
							) {
								const got = getParametersAndReturnType(suggest.type.substring(4));
								const params = got.params;
								const paramSnippets = [];
								for (let i = 0; i < params.length; i++) {
									let param = params[i].trim();
									if (param) {
										param = param.replace('${', '\\${').replace('}', '\\}');
										if (config['useCodeSnippetsOnFunctionSuggestWithoutType']) {
											if (param.includes(' ')) {
												// Separate the variable name from the type
												param = param.substr(0, param.indexOf(' '));
											}
										}
										paramSnippets.push('${' + (i + 1) + ':' + param + '}');
									}
								}
								item.insertText = new vscode.SnippetString(
									suggest.name + '(' + paramSnippets.join(', ') + ')'
								);
							}
							if (
								config['useCodeSnippetsOnFunctionSuggest'] &&
								suggest.class === 'type' &&
								suggest.type.startsWith('func(')
							) {
								const { params, returnType } = getParametersAndReturnType(suggest.type.substring(4));
								const paramSnippets = [];
								for (let i = 0; i < params.length; i++) {
									let param = params[i].trim();
									if (param) {
										param = param.replace('${', '\\${').replace('}', '\\}');
										if (!param.includes(' ')) {
											// If we don't have an argument name, we need to create one
											param = 'arg' + (i + 1) + ' ' + param;
										}
										const arg = param.substr(0, param.indexOf(' '));
										paramSnippets.push(
											'${' +
												(i + 1) +
												':' +
												arg +
												'}' +
												param.substr(param.indexOf(' '), param.length)
										);
									}
								}
								item.insertText = new vscode.SnippetString(
									suggest.name +
										'(func(' +
										paramSnippets.join(', ') +
										') {\n	$' +
										(params.length + 1) +
										'\n})' +
										returnType
								);
							}

							if (
								wordAtPosition &&
								wordAtPosition.start.character === 0 &&
								suggest.class === 'type' &&
								!goBuiltinTypes.has(suggest.name)
							) {
								const auxItem = new vscode.CompletionItem(
									suggest.name + ' method',
									vscode.CompletionItemKind.Snippet
								);
								auxItem.label = 'func (*' + suggest.name + ')';
								auxItem.filterText = suggest.name;
								auxItem.detail = 'Method snippet';
								auxItem.sortText = 'b';
								const prefix = 'func (' + suggest.name[0].toLowerCase() + ' *' + suggest.name + ')';
								const snippet = prefix + ' ${1:methodName}(${2}) ${3} {\n\t$0\n}';
								auxItem.insertText = new vscode.SnippetString(snippet);
								suggestions.push(auxItem);
							}

							// Add same sortText to all suggestions from gocode so that they appear before the unimported packages
							item.sortText = 'a';
							suggestions.push(item);
						}
					}

					// Add importable packages matching currentword to suggestions
					if (includeUnimportedPkgs && !this.isGoMod && !areCompletionsForPackageSymbols) {
						suggestions = suggestions.concat(
							getPackageCompletions(document, currentWord, this.pkgsList, packageSuggestions)
						);
					}

					resolve(suggestions);
				} catch (e) {
					reject(e);
				}
			});
			if (p.pid) {
				p.stdin.end(inputText);
			}
		});
	}