Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT / WIP - DO NOT MERGE] Show filtered builtins documentation #1174

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/documentation/api/ApiNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ const buildSignature = (
};

const isInitOrOtherNonDunderMethod = (c: ApiDocsEntry) =>
!c.name.endsWith("__") || c.name === "__init__";
!c.name.endsWith("__") || c.name === "__init__" || c.name === "__new__";

const filterChildren = (
children: ApiDocsEntry[] | undefined
Expand Down Expand Up @@ -340,21 +340,28 @@ const classToInstanceMap: Record<string, string> = {
MicroBitAnalogDigitalPin: "pin0",
Image: "Image.HEART",
uname_result: "uname()",
str: `"hello, world"`,
list: `[1, 2, 3]`,
};

const getDragPasteData = (fullName: string, kind: string): PasteContext => {
let parts = fullName.split(".").filter((p) => p !== "__init__");
let parts = fullName
.split(".")
.filter((p) => p !== "__init__" && p !== "__new__");
// Heuristic identification of e.g. Image.HEART. Sufficient for MicroPython API.
if (!parts[parts.length - 1].match(/^[A-Z0-9_]+$/)) {
parts = parts.map((p) => classToInstanceMap[p] ?? p);
}
const isMicrobit = parts[0] === "microbit";
const nameAsWeImportIt = isMicrobit ? parts.slice(1) : parts;
const isBuiltin = parts[0] === "builtins";
const nameAsWeImportIt = isMicrobit || isBuiltin ? parts.slice(1) : parts;
const code = nameAsWeImportIt.join(".") + (kind === "function" ? "()" : "");
const requiredImport = isMicrobit
const requiredImport = isBuiltin
? undefined
: isMicrobit
? `from microbit import *`
: `import ${parts[0]}`;
const full = `${requiredImport}\n${code}`;
const full = [requiredImport, code].filter((x) => !!x).join("\n");
return {
code,
codeWithImports: full,
Expand Down
21 changes: 21 additions & 0 deletions src/documentation/api/apidocs-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,24 @@ export const moduleAndApiFromId = (id: string) => {
apiId,
};
};

export const filterOutUndocumentedBuiltins = (input: ApiDocsContent) => {
const recursiveFilter = (
entries: ApiDocsEntry[] | undefined
): ApiDocsEntry[] | undefined => {
if (!entries) {
return;
}
return entries.filter((entry) => {
const hasDocstring = !!entry.docString;
if (hasDocstring) {
entry.children = recursiveFilter(entry.children);
}
return hasDocstring;
});
};

input.content.builtins.children = recursiveFilter(
input.content.builtins.children
);
};
6 changes: 5 additions & 1 deletion src/documentation/documentation-hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { apiDocs, ApiDocsContent } from "../language-server/apidocs";
import { useLanguageServerClient } from "../language-server/language-server-hooks";
import { useLogging } from "../logging/logging-hooks";
import { useSettings } from "../settings/settings";
import { pullModulesToTop } from "./api/apidocs-util";
import {
filterOutUndocumentedBuiltins,
pullModulesToTop,
} from "./api/apidocs-util";
import dragImage from "./drag-image.svg";
import { fetchIdeas } from "./ideas/content";
import { Idea } from "./ideas/model";
Expand Down Expand Up @@ -72,6 +75,7 @@ const useApiDocumentation = (): ApiDocsContent | undefined => {
if (client) {
const docs = await apiDocs(client);
pullModulesToTop(docs);
filterOutUndocumentedBuiltins(docs);
if (!ignore) {
setApiDocs(docs);
}
Expand Down
38 changes: 34 additions & 4 deletions src/editor/codemirror/CodeMirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
lineNumbers,
ViewUpdate,
} from "@codemirror/view";
import { useEffect, useMemo, useRef } from "react";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { useIntl } from "react-intl";
import { lineNumFromUint8Array } from "../../common/text-util";
import useActionFeedback from "../../common/use-action-feedback";
Expand Down Expand Up @@ -40,6 +40,7 @@ import { languageServer } from "./language-server/view";
import { lintGutter } from "./lint/lint";
import { codeStructure } from "./structure-highlighting";
import themeExtensions from "./themeExtensions";
import { ApiDocsEntry } from "../../language-server/apidocs";

interface CodeMirrorProps {
className?: string;
Expand Down Expand Up @@ -80,7 +81,32 @@ const CodeMirror = ({
const logging = useLogging();
const actionFeedback = useActionFeedback();
const [sessionSettings, setSessionSettings] = useSessionSettings();
const { apiReferenceMap } = useDocumentation();
const { apiReferenceMap, api } = useDocumentation();

const showLinkToBuiltins = useCallback(
(id: string) => {
const idParts = id.split(".");

const recursiveFind = (
entries: ApiDocsEntry[] | undefined,
index: number = 1
): ApiDocsEntry | undefined => {
if (!entries) {
return;
}
return entries.find((entry) => {
const match = entry.id === idParts.slice(0, index + 1).join(".");
if (match) {
recursiveFind(entry.children, index + 1);
}
return match;
});
};

return !!recursiveFind(api?.content.builtins.children);
},
[api?.content.builtins.children]
);

// Reset undo/redo events on file change.
useEffect(() => {
Expand Down Expand Up @@ -141,7 +167,8 @@ const CodeMirror = ({
signatureHelp: {
automatic: parameterHelpOption === "automatic",
},
}
},
showLinkToBuiltins
)
: [],
codeStructure(options.codeStructureOption),
Expand Down Expand Up @@ -172,6 +199,7 @@ const CodeMirror = ({
parameterHelpOption,
uri,
apiReferenceMap,
showLinkToBuiltins,
]);
useEffect(() => {
// Do this separately as we don't want to destroy the view whenever options needed for initialization change.
Expand Down Expand Up @@ -199,7 +227,8 @@ const CodeMirror = ({
signatureHelp: {
automatic: parameterHelpOption === "automatic",
},
}
},
showLinkToBuiltins
)
: [],
codeStructure(options.codeStructureOption),
Expand All @@ -215,6 +244,7 @@ const CodeMirror = ({
logging,
uri,
apiReferenceMap,
showLinkToBuiltins,
]);

const { location } = selection;
Expand Down
18 changes: 14 additions & 4 deletions src/editor/codemirror/language-server/autocompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import { nameFromSignature, removeFullyQualifiedName } from "./names";
import { offsetToPosition } from "./positions";
import { escapeRegExp } from "./regexp-util";
import { ShowLinkToBuiltins } from "./view";

// Used to find the true start of the completion. Doesn't need to exactly match
// any language's identifier definition.
Expand All @@ -44,7 +45,8 @@ type AugmentedCompletion = Completion & { item: CompletionItem };
export const autocompletion = (
intl: IntlShape,
logging: Logging,
apiReferenceMap: ApiReferenceMap
apiReferenceMap: ApiReferenceMap,
showLinkToBuiltins: ShowLinkToBuiltins
) =>
cmAutocompletion({
override: [
Expand Down Expand Up @@ -76,7 +78,8 @@ export const autocompletion = (
const documentationResolver = createDocumentationResolver(
client,
intl,
apiReferenceMap
apiReferenceMap,
showLinkToBuiltins
);
const results = await client.completionRequest({
textDocument: {
Expand Down Expand Up @@ -145,7 +148,8 @@ const createDocumentationResolver =
(
client: LanguageServerClient,
intl: IntlShape,
apiReferenceMap: ApiReferenceMap
apiReferenceMap: ApiReferenceMap,
showLinkToBuiltins: ShowLinkToBuiltins
) =>
async (completion: Completion): Promise<Node> => {
let documentation: string | LSP.MarkupContent | undefined;
Expand All @@ -171,7 +175,13 @@ const createDocumentationResolver =
if (id) {
const referenceLink = getLinkToReference(id, apiReferenceMap);
code.innerText = removeFullyQualifiedName(code.innerText);
return wrapWithDocumentationButton(intl, node, id, referenceLink);
return wrapWithDocumentationButton(
intl,
node,
id,
referenceLink,
showLinkToBuiltins
);
}
}
return node;
Expand Down
9 changes: 7 additions & 2 deletions src/editor/codemirror/language-server/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { moduleAndApiFromId } from "../../../documentation/api/apidocs-util";
import { ApiReferenceMap } from "../../../documentation/mapping/content";
import { splitDocString } from "./docstrings";
import "./documentation.css";
import { ShowLinkToBuiltins } from "./view";

export const enum DocSections {
Summary = 1 << 0,
Expand Down Expand Up @@ -118,7 +119,8 @@ export const wrapWithDocumentationButton = (
intl: IntlShape,
child: Element,
id: string,
referenceLink: string | undefined
referenceLink: string | undefined,
showLinkToBuiltins: ShowLinkToBuiltins
): Element => {
const docsAndActions = document.createElement("div");
docsAndActions.style.display = "flex";
Expand Down Expand Up @@ -154,7 +156,10 @@ export const wrapWithDocumentationButton = (
// We don't have documentation for builtins yet,
// so there is nothing to link to.
const { pythonModuleName } = moduleAndApiFromId(id);
if (pythonModuleName !== "builtins") {
if (
pythonModuleName !== "builtins" ||
(pythonModuleName === "builtins" && showLinkToBuiltins(id))
) {
const apiAnchor = createStyledAnchorElement();
apiAnchor.textContent = intl.formatMessage({ id: "api-tab" });
apiAnchor.onclick = (e) => {
Expand Down
25 changes: 19 additions & 6 deletions src/editor/codemirror/language-server/signatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
} from "./documentation";
import { nameFromSignature, removeFullyQualifiedName } from "./names";
import { offsetToPosition } from "./positions";
import { ShowLinkToBuiltins } from "./view";

export const setSignatureHelpRequestPosition = StateEffect.define<number>({});

Expand Down Expand Up @@ -121,7 +122,8 @@ const openSignatureHelp: Command = (view: EditorView) => {
export const signatureHelp = (
intl: IntlShape,
automatic: boolean,
apiReferenceMap: ApiReferenceMap
apiReferenceMap: ApiReferenceMap,
showLinkToBuiltins: ShowLinkToBuiltins
) => {
const signatureHelpTooltipField = StateField.define<SignatureHelpState>({
create: () => new SignatureHelpState(-1, null),
Expand Down Expand Up @@ -162,7 +164,9 @@ export const signatureHelp = (
create: () => {
const dom = document.createElement("div");
dom.className = "cm-signature-tooltip";
dom.appendChild(formatSignatureHelp(result, apiReferenceMap));
dom.appendChild(
formatSignatureHelp(result, apiReferenceMap, showLinkToBuiltins)
);
return { dom };
},
};
Expand Down Expand Up @@ -219,7 +223,8 @@ export const signatureHelp = (

const formatSignatureHelp = (
help: SignatureHelp,
apiReferenceMap: ApiReferenceMap
apiReferenceMap: ApiReferenceMap,
showLinkToBuiltins: ShowLinkToBuiltins
): Node => {
const { activeSignature: activeSignatureIndex, signatures } = help;
// We intentionally do something minimal here to minimise distraction.
Expand Down Expand Up @@ -253,7 +258,8 @@ export const signatureHelp = (
to,
signatureDoc,
activeParameterDoc,
apiReferenceMap
apiReferenceMap,
showLinkToBuiltins
);
};

Expand All @@ -263,7 +269,8 @@ export const signatureHelp = (
to: number,
signatureDoc: string | MarkupContent | undefined,
activeParameterDoc: string | MarkupContent | undefined,
apiReferenceMap: ApiReferenceMap
apiReferenceMap: ApiReferenceMap,
showLinkToBuiltins: ShowLinkToBuiltins
): Node => {
let before = label.substring(0, from);
const id = nameFromSignature(before);
Expand Down Expand Up @@ -301,7 +308,13 @@ export const signatureHelp = (
);
}
const referenceLink = getLinkToReference(id, apiReferenceMap);
return wrapWithDocumentationButton(intl, parent, id, referenceLink);
return wrapWithDocumentationButton(
intl,
parent,
id,
referenceLink,
showLinkToBuiltins
);
};

return [
Expand Down
14 changes: 11 additions & 3 deletions src/editor/codemirror/language-server/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ interface Options {
};
}

export type ShowLinkToBuiltins = (id: string) => boolean;

/**
* Extensions that make use of a language server client.
*
Expand All @@ -88,13 +90,19 @@ export function languageServer(
intl: IntlShape,
logging: Logging,
apiReferenceMap: ApiReferenceMap,
options: Options
options: Options,
showLinkToBuiltins: ShowLinkToBuiltins
) {
return [
uriFacet.of(uri),
clientFacet.of(client),
ViewPlugin.define((view) => new LanguageServerView(view)),
signatureHelp(intl, options.signatureHelp.automatic, apiReferenceMap),
autocompletion(intl, logging, apiReferenceMap),
signatureHelp(
intl,
options.signatureHelp.automatic,
apiReferenceMap,
showLinkToBuiltins
),
autocompletion(intl, logging, apiReferenceMap, showLinkToBuiltins),
];
}
1 change: 1 addition & 0 deletions src/language-server/apidocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const apiDocs = async (
// For now, this omits a lot of modules that have stubs
// derived from typeshed with no docs.
// Note: "audio" is covered under micro:bit.
"builtins",
"gc",
"log",
"machine",
Expand Down
18 changes: 9 additions & 9 deletions src/micropython/main/typeshed.de.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/micropython/main/typeshed.en.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions src/micropython/main/typeshed.es-es.json

Large diffs are not rendered by default.