diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 03293322..642b4205 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,8 @@ jobs: - name: Set env to beta if: contains(github.ref, '-beta.') || contains(github.ref, '-rc.') run: | - echo "REACT_APP_NAME=Pybricks Code (Beta)" >> $GITHUB_ENV + echo "REACT_APP_NAME=Pybricks Beta" >> $GITHUB_ENV + echo "REACT_APP_SUFFIX=-beta" >> $GITHUB_ENV - uses: actions/setup-node@v1 with: node-version: '12.x' diff --git a/CHANGELOG.md b/CHANGELOG.md index be4d3a28..4cebb7c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ # Changelog +## [Unreleased] + +### Changed +- Changed snippet (autocomplete) content and behavior [issue#471]. +- Changed gutter background color [issue#472]. +- Added *BETA` badge to application icon [support#375]. + +### Fixed +- Fixed auto-indent not working [issue#470]. + ## [1.1.0-beta.2] - 2021-07-06 ## Changed @@ -33,7 +43,11 @@ Prerelease changes are documented at [support#48]. +[issue#470]: https://github.com/pybricks/pybricks-code/issues/470 +[issue#471]: https://github.com/pybricks/pybricks-code/issues/471 +[issue#472]: https://github.com/pybricks/pybricks-code/issues/472 [support#48]: https://github.com/pybricks/support/issues/48 +[support#375]: https://github.com/pybricks/support/issues/375 [support#378]: https://github.com/pybricks/support/issues/378 [v3.0.0]: https://github.com/pybricks/pybricks-micropython/blob/master/CHANGELOG.md#300---2021-06-08 [v3.1.0a1]: https://github.com/pybricks/pybricks-micropython/blob/master/CHANGELOG.md#310a1---2021-06-23 diff --git a/public/logo192-beta.png b/public/logo192-beta.png new file mode 100644 index 00000000..c7e98174 Binary files /dev/null and b/public/logo192-beta.png differ diff --git a/public/logo192m-beta.png b/public/logo192m-beta.png new file mode 100644 index 00000000..f402c9a8 Binary files /dev/null and b/public/logo192m-beta.png differ diff --git a/public/logo512-beta.png b/public/logo512-beta.png new file mode 100644 index 00000000..d6c72776 Binary files /dev/null and b/public/logo512-beta.png differ diff --git a/public/manifest.json b/public/manifest.json index b347bf6a..17901a91 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -3,24 +3,19 @@ "name": "%REACT_APP_NAME%", "icons": [ { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", + "src": "logo192%REACT_APP_SUFFIX%.png", "type": "image/png", "sizes": "192x192", "purpose": "any" }, { - "src": "logo192m.png", + "src": "logo192m%REACT_APP_SUFFIX%.png", "type": "image/png", "sizes": "192x192", "purpose": "maskable" }, { - "src": "logo512.png", + "src": "logo512%REACT_APP_SUFFIX%.png", "type": "image/png", "sizes": "512x512" } diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx index 2a73f764..ef54576a 100644 --- a/src/editor/Editor.tsx +++ b/src/editor/Editor.tsx @@ -42,15 +42,17 @@ monaco.languages.register({ id: pybricksMicroPythonId }); const toDispose = new Array(); toDispose.push( + monaco.languages.setLanguageConfiguration( + pybricksMicroPythonId, + pybricksMicroPython.conf, + ), monaco.languages.setMonarchTokensProvider( pybricksMicroPythonId, pybricksMicroPython.language, ), -); -toDispose.push( monaco.languages.registerCompletionItemProvider( pybricksMicroPythonId, - pybricksMicroPython.completions, + pybricksMicroPython.templateSnippetCompletions, ), ); diff --git a/src/editor/editor.scss b/src/editor/editor.scss index f51ba515..b3f77b17 100644 --- a/src/editor/editor.scss +++ b/src/editor/editor.scss @@ -5,16 +5,6 @@ @import '../variables.scss'; -// make editor match app backgound color - -.#{$ns}-dark .margin-view-overlays { - background-color: $pt-dark-app-background-color; -} - -.margin-view-overlays { - background-color: $pt-app-background-color; -} - // add "BETA" watermark .pb-beta .editor-scrollable::after { diff --git a/src/editor/pybricksMicroPython.ts b/src/editor/pybricksMicroPython.ts index 2865df04..160155e3 100644 --- a/src/editor/pybricksMicroPython.ts +++ b/src/editor/pybricksMicroPython.ts @@ -1,4 +1,4 @@ -// Copied from https://github.com/microsoft/monaco-languages/blob/main/src/python/python.ts +// Copied from https://github.com/microsoft/monaco-languages/blob/d7cc098c481059f63d51ce3753975c8ca8ab6030/src/python/python.ts /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -7,23 +7,61 @@ import { monaco } from 'react-monaco-editor'; +export const conf: monaco.languages.LanguageConfiguration = { + comments: { + lineComment: '#', + blockComment: ["'''", "'''"], + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"', notIn: ['string'] }, + { open: "'", close: "'", notIn: ['string', 'comment'] }, + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + ], + onEnterRules: [ + { + beforeText: new RegExp( + '^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$', + ), + action: { indentAction: monaco.languages.IndentAction.Indent }, + }, + ], + folding: { + offSide: true, + markers: { + start: new RegExp('^\\s*#region\\b'), + end: new RegExp('^\\s*#endregion\\b'), + }, + }, +}; + export const language = { defaultToken: '', tokenPostfix: '.python', - keywords: [ - // This section is the result of running - // `for k in keyword.kwlist: print(' "' + k + '",')` in a Python REPL, - // though note that the output from Python 3 is not a strict superset of the - // output from Python 2. - 'False', // promoted to keyword.kwlist in Python 3 - 'None', // promoted to keyword.kwlist in Python 3 - 'True', // promoted to keyword.kwlist in Python 3 + // https://docs.python.org/3/reference/lexical_analysis.html#keywords + keywords: >[ + 'False', + 'None', + 'True', 'and', 'as', 'assert', - 'async', // new in Python 3 - 'await', // new in Python 3 + 'async', + 'await', 'break', 'class', 'continue', @@ -32,7 +70,6 @@ export const language = { 'elif', 'else', 'except', - 'exec', // Python 2, but not 3. 'finally', 'for', 'from', @@ -42,38 +79,32 @@ export const language = { 'in', 'is', 'lambda', - 'nonlocal', // new in Python 3 + 'nonlocal', 'not', 'or', 'pass', - 'print', // Python 2, but not 3. 'raise', 'return', 'try', 'while', 'with', 'yield', + ], - 'int', - 'float', - 'long', - 'complex', - 'hex', - + // https://docs.python.org/3/library/functions.html#built-in-funcs + builtins: >[ 'abs', 'all', 'any', - 'apply', - 'basestring', + 'ascii', 'bin', 'bool', - 'buffer', + 'breakpoint', 'bytearray', + 'bytes', 'callable', 'chr', 'classmethod', - 'cmp', - 'coerce', 'compile', 'complex', 'delattr', @@ -82,9 +113,9 @@ export const language = { 'divmod', 'enumerate', 'eval', - 'execfile', - 'file', + 'exec', 'filter', + 'float', 'format', 'frozenset', 'getattr', @@ -92,15 +123,16 @@ export const language = { 'hasattr', 'hash', 'help', + 'hex', 'id', 'input', - 'intern', + 'int', 'isinstance', 'issubclass', 'iter', 'len', - 'locals', 'list', + 'locals', 'map', 'max', 'memoryview', @@ -115,9 +147,6 @@ export const language = { 'property', 'reversed', 'range', - 'raw_input', - 'reduce', - 'reload', 'repr', 'reversed', 'round', @@ -132,21 +161,8 @@ export const language = { 'super', 'tuple', 'type', - 'unichr', - 'unicode', 'vars', - 'xrange', 'zip', - - '__dict__', - '__methods__', - '__members__', - '__class__', - '__bases__', - '__name__', - '__mro__', - '__subclasses__', - '__init__', '__import__', ], @@ -173,6 +189,7 @@ export const language = { { cases: { '@keywords': 'keyword', + '@builtins': 'support.function', '@default': 'identifier', }, }, @@ -236,67 +253,87 @@ export const language = { }, }; -export const completions = { - provideCompletionItems: (_model, position, _context, _token) => { - return { - suggestions: [ - { - label: 'technichub', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: `from pybricks.hubs import TechnicHub -from pybricks.pupdevices import Motor -from pybricks.parameters import Port, Stop, Color -from pybricks.tools import wait +function template(hubName: string, devices: string[]): string { + return `from pybricks.hubs import ${hubName} +from pybricks.pupdevices import ${devices.join(', ')} +from pybricks.parameters import Button, Color, Direction, Port, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch -hub = TechnicHub()`, - range: monaco.Range.fromPositions(position), - }, - { - label: 'cityhub', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: `from pybricks.hubs import CityHub -from pybricks.pupdevices import Motor -from pybricks.parameters import Port, Stop, Color -from pybricks.tools import wait +hub = ${hubName}()`; +} -hub = CityHub()`, - range: monaco.Range.fromPositions(position), - }, - { - label: 'movehub', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: `from pybricks.hubs import MoveHub -from pybricks.pupdevices import Motor -from pybricks.parameters import Port, Stop, Color -from pybricks.tools import wait +const templateSnippets: Array< + Required< + Pick + > & { label: string } +> = [ + { + label: 'technichub', + documentation: 'Template for Technic hub program.', + insertText: template('TechnicHub', ['Motor']), + }, + { + label: 'cityhub', + documentation: 'Template for City hub program.', + insertText: template('CityHub', ['DCMotor', 'Light']), + }, + { + label: 'movehub', + documentation: 'Template for BOOST Move hub program.', + insertText: template('MoveHub', ['Motor', 'ColorDistanceSensor']), + }, + { + label: 'primehub', + documentation: 'Template for SPIKE Prime program.', + insertText: template('PrimeHub', [ + 'Motor', + 'ColorSensor', + 'UltrasonicSensor', + 'ForceSensor', + ]), + }, + { + label: 'inventorhub', + documentation: 'Template for MINDSTORMS Robot Inventor hub program.', + insertText: template('InventorHub', [ + 'Motor', + 'ColorSensor', + 'UltrasonicSensor', + ]), + }, +]; -hub = MoveHub()`, - range: monaco.Range.fromPositions(position), - }, - { - label: 'primehub', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: `from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor, ColorSensor, ForceSensor, UltrasonicSensor -from pybricks.parameters import Port, Stop, Color, Button -from pybricks.tools import wait +export const templateSnippetCompletions = { + provideCompletionItems: (model, position, _context, _token) => { + // templates snippets are only available on the first line + if (position.lineNumber !== 1) { + return undefined; + } -hub = PrimeHub()`, - range: monaco.Range.fromPositions(position), - }, - { - label: 'inventorhub', - kind: monaco.languages.CompletionItemKind.Snippet, - insertText: `from pybricks.hubs import InventorHub -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor -from pybricks.parameters import Port, Stop, Color, Button -from pybricks.tools import wait - -hub = InventorHub()`, - range: monaco.Range.fromPositions(position), - }, - ], + const range = { + startLineNumber: position.lineNumber, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column, }; + + const textUntilPosition = model.getValueInRange(range); + + const items = templateSnippets + .filter((x) => x.label.startsWith(textUntilPosition)) + .map((x) => ({ + detail: x.insertText, + kind: monaco.languages.CompletionItemKind.Snippet, + range, + ...x, + })); + + if (!items) { + return undefined; + } + + return { suggestions: items }; }, };