kendalllane | 9ca0744 | 2019-06-10 09:18:14 -0700 | [diff] [blame] | 1 | // Copyright 2019 The Bazel Authors. All rights reserved. |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.skydoc.rendering; |
| 16 | |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 17 | import com.google.common.collect.ImmutableList; |
| 18 | import com.google.common.collect.Lists; |
| 19 | import com.google.common.collect.Maps; |
kendalllane | 267df73 | 2019-06-05 13:40:19 -0700 | [diff] [blame] | 20 | import com.google.devtools.build.lib.syntax.Printer; |
| 21 | import com.google.devtools.build.lib.syntax.Printer.BasePrinter; |
Googler | 3fcfbe1 | 2019-08-28 08:10:11 -0700 | [diff] [blame] | 22 | import com.google.devtools.build.lib.syntax.StarlarkFunction; |
kendalllane | 267df73 | 2019-06-05 13:40:19 -0700 | [diff] [blame] | 23 | import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.FunctionParamInfo; |
Googler | 3fcfbe1 | 2019-08-28 08:10:11 -0700 | [diff] [blame] | 24 | import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo; |
gregce | 3ec8d81 | 2020-06-18 08:05:01 -0700 | [diff] [blame^] | 25 | import com.google.devtools.starlark.common.DocstringUtils; |
| 26 | import com.google.devtools.starlark.common.DocstringUtils.DocstringInfo; |
| 27 | import com.google.devtools.starlark.common.DocstringUtils.DocstringParseError; |
| 28 | import com.google.devtools.starlark.common.DocstringUtils.ParameterDoc; |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 29 | import java.util.List; |
| 30 | import java.util.Map; |
kendalllane | 267df73 | 2019-06-05 13:40:19 -0700 | [diff] [blame] | 31 | import javax.annotation.Nullable; |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 32 | |
kendalllane | 9ca0744 | 2019-06-10 09:18:14 -0700 | [diff] [blame] | 33 | /** Contains a number of utility methods for functions and parameters. */ |
| 34 | public final class FunctionUtil { |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 35 | /** |
Googler | 3fcfbe1 | 2019-08-28 08:10:11 -0700 | [diff] [blame] | 36 | * Create and return a {@link StarlarkFunctionInfo} object encapsulating information obtained from |
| 37 | * the given function and from its parsed docstring. |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 38 | * |
| 39 | * @param functionName the name of the function in the target scope. (Note this is not necessarily |
| 40 | * the original exported function name; the function may have been renamed in the target |
| 41 | * Starlark file's scope) |
adonovan | f34b805 | 2020-01-16 09:42:22 -0800 | [diff] [blame] | 42 | * @param fn the function object |
kendalllane | 9ca0744 | 2019-06-10 09:18:14 -0700 | [diff] [blame] | 43 | * @throws com.google.devtools.build.skydoc.rendering.DocstringParseException if the function's |
| 44 | * docstring is malformed |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 45 | */ |
adonovan | f34b805 | 2020-01-16 09:42:22 -0800 | [diff] [blame] | 46 | public static StarlarkFunctionInfo fromNameAndFunction(String functionName, StarlarkFunction fn) |
| 47 | throws DocstringParseException { |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 48 | String functionDescription = ""; |
| 49 | Map<String, String> paramNameToDocMap = Maps.newLinkedHashMap(); |
| 50 | |
adonovan | f34b805 | 2020-01-16 09:42:22 -0800 | [diff] [blame] | 51 | String doc = fn.getDocumentation(); |
| 52 | if (doc != null) { |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 53 | List<DocstringParseError> parseErrors = Lists.newArrayList(); |
adonovan | f34b805 | 2020-01-16 09:42:22 -0800 | [diff] [blame] | 54 | DocstringInfo docstringInfo = DocstringUtils.parseDocstring(doc, parseErrors); |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 55 | if (!parseErrors.isEmpty()) { |
adonovan | f34b805 | 2020-01-16 09:42:22 -0800 | [diff] [blame] | 56 | throw new DocstringParseException(functionName, fn.getLocation(), parseErrors); |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 57 | } |
| 58 | functionDescription += docstringInfo.getSummary(); |
| 59 | if (!docstringInfo.getSummary().isEmpty() && !docstringInfo.getLongDescription().isEmpty()) { |
| 60 | functionDescription += "\n\n"; |
| 61 | } |
| 62 | functionDescription += docstringInfo.getLongDescription(); |
| 63 | for (ParameterDoc paramDoc : docstringInfo.getParameters()) { |
| 64 | paramNameToDocMap.put(paramDoc.getParameterName(), paramDoc.getDescription()); |
| 65 | } |
| 66 | } |
adonovan | f34b805 | 2020-01-16 09:42:22 -0800 | [diff] [blame] | 67 | List<FunctionParamInfo> paramsInfo = parameterInfos(fn, paramNameToDocMap); |
Googler | 3fcfbe1 | 2019-08-28 08:10:11 -0700 | [diff] [blame] | 68 | return StarlarkFunctionInfo.newBuilder() |
kendalllane | 9ca0744 | 2019-06-10 09:18:14 -0700 | [diff] [blame] | 69 | .setFunctionName(functionName) |
| 70 | .setDocString(functionDescription) |
kendalllane | 1dbbc014 | 2019-06-14 07:50:24 -0700 | [diff] [blame] | 71 | .addAllParameter(paramsInfo) |
kendalllane | 9ca0744 | 2019-06-10 09:18:14 -0700 | [diff] [blame] | 72 | .build(); |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 73 | } |
| 74 | |
kendalllane | 267df73 | 2019-06-05 13:40:19 -0700 | [diff] [blame] | 75 | /** Constructor to be used for normal parameters. */ |
| 76 | public static FunctionParamInfo forParam( |
| 77 | String name, String docString, @Nullable Object defaultValue) { |
| 78 | FunctionParamInfo.Builder paramBuilder = |
| 79 | FunctionParamInfo.newBuilder().setName(name).setDocString(docString); |
| 80 | if (defaultValue == null) { |
| 81 | paramBuilder.setMandatory(true); |
| 82 | } else { |
| 83 | BasePrinter printer = Printer.getSimplifiedPrinter(); |
| 84 | printer.repr(defaultValue); |
| 85 | String defaultValueString = printer.toString(); |
| 86 | |
| 87 | if (defaultValueString.isEmpty()) { |
| 88 | defaultValueString = "{unknown object}"; |
| 89 | } |
| 90 | paramBuilder.setDefaultValue(defaultValueString).setMandatory(false); |
| 91 | } |
| 92 | return paramBuilder.build(); |
| 93 | } |
| 94 | |
| 95 | /** Constructor to be used for *args or **kwargs. */ |
| 96 | public static FunctionParamInfo forSpecialParam(String name, String docString) { |
| 97 | return FunctionParamInfo.newBuilder() |
| 98 | .setName(name) |
| 99 | .setDocString(docString) |
| 100 | .setMandatory(false) |
| 101 | .build(); |
| 102 | } |
| 103 | |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 104 | private static List<FunctionParamInfo> parameterInfos( |
adonovan | cac48fe | 2020-04-23 12:31:21 -0700 | [diff] [blame] | 105 | StarlarkFunction fn, Map<String, String> parameterDoc) { |
| 106 | List<String> names = fn.getParameterNames(); |
| 107 | int nparams = names.size(); |
| 108 | int kwargsIndex = fn.hasKwargs() ? --nparams : -1; |
| 109 | int varargsIndex = fn.hasVarargs() ? --nparams : -1; |
| 110 | // Inv: nparams is number of regular parameters. |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 111 | |
adonovan | cac48fe | 2020-04-23 12:31:21 -0700 | [diff] [blame] | 112 | ImmutableList.Builder<FunctionParamInfo> infos = ImmutableList.builder(); |
| 113 | for (int i = 0; i < names.size(); i++) { |
| 114 | String name = names.get(i); |
| 115 | FunctionParamInfo info; |
| 116 | if (i == varargsIndex) { |
| 117 | // *args |
| 118 | String doc = parameterDoc.getOrDefault("*" + name, ""); |
| 119 | info = forSpecialParam(name, doc); |
| 120 | } else if (i == kwargsIndex) { |
| 121 | // **kwargs |
| 122 | String doc = parameterDoc.getOrDefault("**" + name, ""); |
| 123 | info = forSpecialParam(name, doc); |
| 124 | } else { |
| 125 | // regular parameter |
| 126 | String doc = parameterDoc.getOrDefault(name, ""); |
| 127 | info = forParam(name, doc, fn.getDefaultValue(i)); |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 128 | } |
adonovan | cac48fe | 2020-04-23 12:31:21 -0700 | [diff] [blame] | 129 | infos.add(info); |
cparsons | a249de1 | 2019-02-25 12:00:10 -0800 | [diff] [blame] | 130 | } |
adonovan | cac48fe | 2020-04-23 12:31:21 -0700 | [diff] [blame] | 131 | return infos.build(); |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 132 | } |
cparsons | 2b98ef7 | 2018-11-07 14:05:11 -0800 | [diff] [blame] | 133 | } |