├── .gitattributes ├── .gitignore ├── .testignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── ThirdPartyNotices.txt ├── assets ├── Preview.svg ├── PreviewOnRightPane_16x.svg ├── PreviewOnRightPane_16x_dark.svg ├── Preview_inverse.svg ├── ViewSource.svg ├── ViewSource_inverse.svg ├── images │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ ├── ui-bg_flat_10_000000_40x100.png │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ ├── ui-bg_glass_65_ffffff_1x400.png │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ ├── ui-icons_222222_256x240.png │ ├── ui-icons_228ef1_256x240.png │ ├── ui-icons_ef8c08_256x240.png │ ├── ui-icons_ffd27a_256x240.png │ └── ui-icons_ffffff_256x240.png ├── jquery-ui.min.css ├── pytutor.common.css └── pytutor.theme.css ├── gulpfile.js ├── images ├── previewDemo.gif └── settingDemo.gif ├── package-lock.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── package.nls.zh-tw.json ├── preview-src ├── README.md ├── datas.ts ├── events.ts ├── index.ts ├── lib │ ├── d3.v2.min.js │ ├── jquery-3.0.0.min.js │ ├── jquery-ui-1.11.4 │ │ ├── external │ │ │ └── jquery │ │ │ │ └── jquery.js │ │ ├── images │ │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_228ef1_256x240.png │ │ │ ├── ui-icons_ef8c08_256x240.png │ │ │ ├── ui-icons_ffd27a_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ ├── index.html │ │ ├── jquery-ui.css │ │ ├── jquery-ui.js │ │ ├── jquery-ui.min.css │ │ ├── jquery-ui.min.js │ │ ├── jquery-ui.structure.css │ │ ├── jquery-ui.structure.min.css │ │ ├── jquery-ui.theme.css │ │ └── jquery-ui.theme.min.css │ ├── jquery.ba-bbq.js │ └── jquery.jsPlumb-1.3.10-all-min.js ├── messaging.ts ├── package-lock.json ├── package.json ├── pytutor.ts ├── tsconfig.json ├── typings.json ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js ├── python-icon.png ├── pythonFiles └── pydev │ ├── debugger.py │ ├── launcher.py │ ├── pg_encoder.py │ ├── pg_logger.py │ └── util.py ├── src ├── commands │ ├── index.ts │ ├── refreshPreview.ts │ ├── showPreview.ts │ ├── showSource.ts │ └── toggleLock.ts ├── common │ ├── commandManager.ts │ ├── dispose.ts │ ├── file.ts │ ├── helpers.ts │ ├── is.ts │ ├── lazy.ts │ ├── logger.ts │ ├── net │ │ └── socket │ │ │ └── socketStream.ts │ └── platform │ │ └── pathUtils.ts ├── debugger │ ├── common │ │ ├── contracts.ts │ │ └── utils.ts │ ├── debugClients │ │ ├── baseDebugClient.ts │ │ ├── launcherProvider.ts │ │ └── localDebugClient.ts │ ├── debugServers │ │ ├── baseDebugServer.ts │ │ └── localDebugServer.ts │ ├── proxyCommands.ts │ ├── pythonProcess.ts │ └── pythonProcessCallbackHandler.ts ├── extension.ts ├── features │ ├── preview.ts │ ├── previewConfig.ts │ ├── previewContentProvider.ts │ ├── previewManager.ts │ └── pythonOutput.ts └── test │ ├── common │ └── net │ │ └── socketStream.test.ts │ ├── extension.test.ts │ └── index.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | package.json text eol=lf 3 | package.lock.json text eol=lf 4 | 5 | preview-src/package.json text eol=lf 6 | preview-src/package-lock.json text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | **/node_modules 3 | .vscode-test/ 4 | *.vsix 5 | 6 | assets/index.js 7 | preview-src/node_modules 8 | preview-src/typings 9 | pythonFiles/pydev/__pycache__ 10 | python-preview -------------------------------------------------------------------------------- /.testignore: -------------------------------------------------------------------------------- 1 | .gitattributes 2 | .gitignore 3 | .testignore 4 | .vscodeignore 5 | gulpfile.js 6 | tsconfig.json 7 | tslint.json 8 | vsc-extension-quickstart.md 9 | 10 | .vscode/** 11 | .vscode 12 | .vscode-test/** 13 | .vscode-test 14 | images 15 | images/** 16 | out/test/** 17 | out/test 18 | out/**/*.map 19 | preview-src/** 20 | preview-src 21 | src/** 22 | src 23 | pythonFiles/pydev/__pycache__/** 24 | pythonFiles/pydev/__pycache__ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "eg2.tslint" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: compile" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--disable-extensions", 28 | "--extensionDevelopmentPath=${workspaceFolder}", 29 | "--extensionTestsPath=${workspaceFolder}/out/test" 30 | ], 31 | "outFiles": [ 32 | "${workspaceFolder}/out/test/**/*.js" 33 | ], 34 | "preLaunchTask": "npm: compile" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .gitattributes 2 | .gitignore 3 | .testignore 4 | gulpfile.js 5 | tsconfig.json 6 | tslint.json 7 | vsc-extension-quickstart.md 8 | ThirdPartyNotices.txt 9 | yarn.lock 10 | 11 | .vscode/** 12 | .vscode-test/** 13 | images/** 14 | out/test/** 15 | out/**/*.map 16 | preview-src/** 17 | python-preview/** 18 | pythonFiles/pydev/__pycache__/** 19 | src/** 20 | testExtension/** -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 0.0.4 2 | - Fix: Fixed cumulativeMode setting invalid error 3 | 4 | ## Version 0.0.3 5 | - Fix: Fixed style error. 6 | 7 | ## Version 0.0.2 8 | - Eliminate unnecessary contents from extension. 9 | - Add third party notices. 10 | - Add settingDemo.gif. 11 | 12 | ## Version 0.0.1 13 | - Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dong Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Preview extension for Visual Studio Code 2 | 3 | A [Visual Studio Code](https://code.visualstudio.com/) [extension](https://marketplace.visualstudio.com/VSCode) with debugging preview support for the [Python language](https://www.python.org/). 4 | 5 | 6 | ## Features 7 | 8 | ![Preview](https://raw.githubusercontent.com/dongli0x00/python-preview/master/images/previewDemo.gif) 9 | 10 | ![Setting](https://raw.githubusercontent.com/dongli0x00/python-preview/master/images/settingDemo.gif) 11 | 12 | ## Requirements 13 | 14 | 1. Install a version of Python 3.6 or Python 2.7. make sure the location of your Python interprter is included in your PATH environment variable. 15 | 2. It's better to install [Python Extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) for Python Intellisense. 16 | 17 | 18 | ## Change Log 19 | 20 | You can checkout all our changes in our [change log](https://github.com/dongli0x00/python-preview/blob/master/CHANGELOG.md). 21 | 22 | ## Thanks 23 | 24 | Thanks to the following projects which I rely on and obtain a number of fresh new ideas from 25 | - [OnlinePythonTutor](https://github.com/pgbovine/OnlinePythonTutor) 26 | - [vscode-python](https://github.com/Microsoft/vscode-python) 27 | - [markdown-language-features](https://github.com/Microsoft/vscode/tree/master/extensions/markdown-language-features) 28 | 29 | Also special thanks to the people that have provided support, testing, etc: 30 | - [JianWei Hong](https://github.com/HongHaiyang) 31 | 32 | And finally thanks to the [Python](https://www.python.org/) development team and community and of course the awesome [vscode](https://github.com/Microsoft/vscode/graphs/contributors) team. -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | pythonVisualizer 2 | 3 | THIRD-PARTY SOFTWARE NOTICES AND INFORMATION 4 | Do Not Translate or Localize 5 | 6 | This project incorporates components from the projects listed below. 7 | 8 | 1. OnlinePythonTutor (https://github.com/pgbovine/OnlinePythonTutor) 9 | 2. vscode-python (https://github.com/Microsoft/vscode-python) 10 | 3. markdown-language-features (https://github.com/Microsoft/vscode/tree/master/extensions/markdown-language-features) 11 | 12 | 13 | %% OnlinePythonTutor NOTICES AND INFORMATION BEGIN HERE 14 | ================================================================================ 15 | The MIT License (MIT) 16 | 17 | Copyright (C) Philip J. Guo (philip@pgbovine.net) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a 20 | copy of this software and associated documentation files (the 21 | "Software"), to deal in the Software without restriction, including 22 | without limitation the rights to use, copy, modify, merge, publish, 23 | distribute, sublicense, and/or sell copies of the Software, and to 24 | permit persons to whom the Software is furnished to do so, subject to 25 | the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be included 28 | in all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 31 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 33 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 34 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 35 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 36 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | 38 | ================================================================================ 39 | END of OnlinePythonTutor NOTICES AND INFORMATION 40 | 41 | %% vscode-python NOTICES AND INFORMATION BEGIN HERE 42 | ================================================================================ 43 | The MIT License (MIT) 44 | 45 | Copyright (c) Microsoft Corporation. All rights reserved. 46 | 47 | MIT License 48 | 49 | Permission is hereby granted, free of charge, to any person obtaining a copy 50 | of this software and associated documentation files (the "Software"), to deal 51 | in the Software without restriction, including without limitation the rights 52 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 53 | copies of the Software, and to permit persons to whom the Software is 54 | furnished to do so, subject to the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be included in all 57 | copies or substantial portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 61 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 62 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 63 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 64 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 65 | SOFTWARE. 66 | 67 | ================================================================================ 68 | END of vscode-python NOTICES AND INFORMATION 69 | 70 | %% markdown-language-features 71 | ================================================================================ 72 | The MIT License (MIT) 73 | 74 | MIT License 75 | 76 | Copyright (c) 2015 - present Microsoft Corporation 77 | 78 | All rights reserved. 79 | 80 | Permission is hereby granted, free of charge, to any person obtaining a copy 81 | of this software and associated documentation files (the "Software"), to deal 82 | in the Software without restriction, including without limitation the rights 83 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 84 | copies of the Software, and to permit persons to whom the Software is 85 | furnished to do so, subject to the following conditions: 86 | 87 | The above copyright notice and this permission notice shall be included in all 88 | copies or substantial portions of the Software. 89 | 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 91 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 93 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 96 | SOFTWARE. 97 | 98 | ================================================================================ 99 | END of markdown-language-features NOTICES AND INFORMATION -------------------------------------------------------------------------------- /assets/Preview.svg: -------------------------------------------------------------------------------- 1 | SwitchToPreview_16x -------------------------------------------------------------------------------- /assets/PreviewOnRightPane_16x.svg: -------------------------------------------------------------------------------- 1 | PreviewInRightPanel_16x -------------------------------------------------------------------------------- /assets/PreviewOnRightPane_16x_dark.svg: -------------------------------------------------------------------------------- 1 | PreviewInRightPanel_16x -------------------------------------------------------------------------------- /assets/Preview_inverse.svg: -------------------------------------------------------------------------------- 1 | SwitchToPreview_16x -------------------------------------------------------------------------------- /assets/ViewSource.svg: -------------------------------------------------------------------------------- 1 | 3 | ]> -------------------------------------------------------------------------------- /assets/ViewSource_inverse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /assets/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /assets/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /assets/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /assets/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /assets/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /assets/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /assets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /assets/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /assets/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /assets/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /assets/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /assets/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/assets/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /assets/pytutor.common.css: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Pytutor通用样式定义 3 | ========================================================================== */ 4 | 5 | body { 6 | font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; 7 | font-size: 16px; 8 | margin-left: auto; 9 | margin-right: auto; 10 | } 11 | 12 | div.ExecutionVisualizer table.visualizer td.vizLayoutTd { 13 | vertical-align: top 14 | } 15 | 16 | /* ========================================================================== 17 | Visualizer左边栏样式定义 18 | ========================================================================== */ 19 | 20 | /** 21 | * 语言标题。 22 | */ 23 | div.ExecutionVisualizer div#langDisplayDiv { 24 | margin-top: 2pt; 25 | margin-bottom: 3pt; 26 | text-align: center; 27 | font-size: 14pt; 28 | } 29 | 30 | /** 31 | * 代码外框。 32 | */ 33 | div.ExecutionVisualizer div#pyCodeOutputDiv { 34 | max-height: 460px; 35 | overflow: auto; 36 | margin-left: auto; 37 | margin-right: auto; 38 | } 39 | 40 | /** 41 | * 代码内框。 42 | */ 43 | div.ExecutionVisualizer table#pyCodeOutput { 44 | /* 代码居中 */ 45 | margin: 6px auto; 46 | border-top: 1px solid; 47 | border-bottom: 1px solid; 48 | padding-top: 3px; 49 | border-spacing: 0px; 50 | border-collapse: separate; 51 | font-family: Consolas; 52 | font-size: 11pt; 53 | line-height: 1.1em; 54 | } 55 | 56 | /** 57 | * 代码所在行。 58 | */ 59 | div.ExecutionVisualizer table#pyCodeOutput td { 60 | white-space: nowrap; 61 | vertical-align: middle; 62 | } 63 | 64 | /** 65 | * 代码内框左沟槽。 66 | */ 67 | div.ExecutionVisualizer #leftCodeGutterSVG { 68 | width: 18px; 69 | min-width: 18px; 70 | height: 0px; 71 | } 72 | 73 | /** 74 | * 代码执行箭头。 75 | */ 76 | div.ExecutionVisualizer #prevLegendArrowSVG, 77 | div.ExecutionVisualizer #curLegendArrowSVG { 78 | width: 18px; 79 | height: 10px; 80 | } 81 | 82 | /** 83 | * 代码行号。 84 | */ 85 | div.ExecutionVisualizer table#pyCodeOutput .lineNo { 86 | padding: 0.2em 0.5em 0.2em 0.3em; 87 | text-align: right; 88 | } 89 | 90 | /** 91 | * 代码。 92 | */ 93 | div.ExecutionVisualizer table#pyCodeOutput .cod { 94 | margin-left: 3px; 95 | padding-left: 7px; 96 | text-align: left; 97 | } 98 | 99 | /** 100 | 图例,用来说明箭头含义。 101 | */ 102 | div.ExecutionVisualizer div#legendDiv { 103 | padding: 0px; 104 | text-align: left; 105 | font-size: 9pt; 106 | } 107 | 108 | /** 109 | * 用来说明Visualier用法。 110 | */ 111 | div.ExecutionVisualizer #codeFooterDocs { 112 | margin-top: 5px; 113 | margin-bottom: 12px; 114 | width: 95%; 115 | font-size: 8pt; 116 | } 117 | 118 | /** 119 | * 代码执行进度条标题。 120 | */ 121 | div.ExecutionVisualizer #executionSliderCaption { 122 | margin-top: 15px; 123 | font-size: 8pt; 124 | } 125 | 126 | /** 127 | * 代码执行进度条。 128 | */ 129 | div.ExecutionVisualizer #executionSlider { 130 | margin-top: 15px; 131 | margin-bottom: 5px; 132 | width: 98%; 133 | } 134 | 135 | /** 136 | * 滑杆以及滑杆手柄。 137 | * 此处指进度条和进度条当前进度点。 138 | */ 139 | div.ExecutionVisualizer .ui-slider .ui-slider-handle { 140 | border: 1px solid; 141 | } 142 | 143 | /** 144 | * 代码执行进度条底部,用来显示断点信息。 145 | */ 146 | div.ExecutionVisualizer #executionSliderFooter { 147 | margin-top: -7px; 148 | } 149 | 150 | /** 151 | * 调试控件。 152 | */ 153 | div.ExecutionVisualizer #vcrControls { 154 | margin: 15px auto; 155 | text-align: center; 156 | } 157 | 158 | /** 159 | * 调试按钮。 160 | */ 161 | div.ExecutionVisualizer #vcrControls button { 162 | margin-left: 2px; 163 | margin-right: 2px; 164 | border: 1px solid; 165 | border-radius: 28px; 166 | cursor: pointer; 167 | } 168 | 169 | /** 170 | * 代码执行当前所处位置。 171 | */ 172 | div.ExecutionVisualizer #vcrControls #curInstr { 173 | margin-left: 4px; 174 | margin-right: 4px; 175 | } 176 | 177 | /** 178 | * 用户输入窗口。 179 | */ 180 | div.ExecutionVisualizer div#rawUserInputDiv { 181 | margin: 5px auto; 182 | border: 1px solid; 183 | padding: 5px; 184 | width: 95%; 185 | text-align: center; 186 | } 187 | 188 | /** 189 | * 错误信息输出窗口。 190 | */ 191 | div.ExecutionVisualier #errorOutput { 192 | margin-bottom: 4px; 193 | padding-top: 2px; 194 | font-size: 11pt; 195 | line-height: 1.5em; 196 | } 197 | 198 | /** 199 | * 调整大小的分隔符。 200 | */ 201 | .ui-resizable-e { 202 | border: 3px solid; 203 | width: 1px; 204 | } 205 | 206 | .ui-resizable-s { 207 | border: 3px solid; 208 | height: 1px; 209 | } 210 | 211 | /* ========================================================================== 212 | Visualizer右边栏样式定义 213 | ========================================================================== */ 214 | 215 | /** 216 | * 程序控制台输出窗口。 217 | */ 218 | div.ExecutionVisualizer div#progOutputs { 219 | margin-left: 13px; 220 | margin-bottom: 3px; 221 | } 222 | 223 | /** 224 | * 程序控制台窗口说明。 225 | */ 226 | div.ExecutionVisualizer #printOutputDocs { 227 | margin-bottom: 3px; 228 | font-size: 8pt; 229 | } 230 | 231 | /** 232 | * 程序标准输出。 233 | */ 234 | div.ExecutionVisualizer #pyStdout { 235 | border: 1px solid; 236 | padding: 3px; 237 | overflow: auto; 238 | resize: none; 239 | font-family: Consolas; 240 | font-size: 10pt; 241 | } 242 | 243 | /** 244 | * 栈和堆的样式定义。 245 | */ 246 | div.ExecutionVisualizer td#stack_td, 247 | div.ExecutionVisualizer td#heap_td { 248 | vertical-align: top; 249 | font-family: Consolas; 250 | font-size: 12pt; 251 | } 252 | 253 | /** 254 | * 全局变量区域和栈。 255 | */ 256 | div.ExecutionVisualizer div#globals_area, 257 | div.ExecutionVisualizer div#stack { 258 | padding-left: 10px; 259 | padding-right: 30px; 260 | } 261 | 262 | /** 263 | * 栈标题。 264 | */ 265 | div.ExecutionVisualizer div#stackHeader { 266 | margin-bottom: 15px; 267 | text-align: right; 268 | font-size: 14pt; 269 | } 270 | 271 | /** 272 | * 栈帧。 273 | */ 274 | div.ExecutionVisualizer div.stackFrame, 275 | div.ExecutionVisualizer div.zombieStackFrame { 276 | margin-bottom: 15px; 277 | padding: 2px 6px 4px; 278 | } 279 | 280 | div.ExecutionVisualizer div.stackFrame { 281 | border-left: 1px solid; 282 | } 283 | 284 | div.ExecutionVisualizer div.zombieStackFrame { 285 | border-left: 1px dotted; 286 | opacity: 0.5; 287 | } 288 | 289 | /** 290 | * 栈帧标题。 291 | */ 292 | div.ExecutionVisualizer div.stackFrameHeader { 293 | margin-top: 4px; 294 | margin-bottom: 3px; 295 | white-space: nowrap; 296 | font-family: Consolas; 297 | font-style: italic; 298 | font-size: 12pt; 299 | } 300 | 301 | /** 302 | * 栈帧中变量与变量值的表格。 303 | */ 304 | div.ExecutionVisualizer .stackFrameVarTable { 305 | /* 右对齐 */ 306 | margin-left: auto; 307 | margin-right: 0px; 308 | padding-left: 3px; 309 | border-collapse: separate; 310 | border-spacing: 2px; 311 | text-align: right; 312 | } 313 | 314 | /** 315 | * 栈帧中的变量。 316 | */ 317 | div.ExecutionVisualizer td.stackFrameVar { 318 | padding: 3px 8px 3px 1px; 319 | text-align: right; 320 | } 321 | 322 | /** 323 | * 栈帧中的变量值。 324 | */ 325 | div.ExecutionVisualizer td.stackFrameValue { 326 | border-bottom: 1px solid; 327 | border-left: 1px solid; 328 | padding: 3px 1px 3px 3px; 329 | vertical-align: middle; 330 | text-align: left; 331 | } 332 | 333 | /** 334 | * 栈帧中返回值。 335 | */ 336 | div.ExecutionVisualizer .retval { 337 | font-size: 8pt; 338 | } 339 | 340 | /** 341 | * 堆。 342 | */ 343 | div.ExecutionVisualizer div#heap { 344 | float: left; 345 | padding-left: 30px; 346 | } 347 | 348 | /** 349 | * 堆标题。 350 | */ 351 | div.ExecutionVisualizer div#heapHeader { 352 | margin-bottom: 15px; 353 | font-size: 14pt; 354 | } 355 | 356 | /** 357 | * 堆对象外框。 358 | */ 359 | div.ExecutionVisualizer table.heapRow { 360 | margin-bottom: 10px; 361 | } 362 | 363 | /** 364 | * 堆对象内框。 365 | */ 366 | div.ExecutionVisualizer td.toplevelHeapObject { 367 | padding: 4px 8px; 368 | } 369 | 370 | /** 371 | * 堆对象。 372 | */ 373 | div.ExecutionVisualizer div.heapObject { 374 | padding-left: 2px; 375 | } 376 | 377 | /** 378 | * 类型标签。 379 | */ 380 | div.ExecutionVisualizer .objectIdLabel, 381 | div.ExecutionVisualizer .typeLabel { 382 | margin-bottom: 2px; 383 | font-size: 8pt; 384 | } 385 | 386 | /** 387 | * 堆中原始类型,比如int类型。 388 | */ 389 | div.ExecutionVisualizer div.heapPrimitive { 390 | padding-left: 4px; 391 | } 392 | 393 | /** 394 | * 原始类型的渲染。 395 | */ 396 | div.ExecutionVisualizer .stringObj, 397 | div.ExecutionVisualizer .customObj, 398 | div.ExecutionVisualizer .funcObj { 399 | white-space: nowrap; 400 | } 401 | 402 | /* ========================================================================== 403 | 基本复合类型的渲染 404 | ========================================================================== */ 405 | 406 | /** 407 | * list、tuple和set类型。 408 | */ 409 | div.ExecutionVisualizer table.listTbl { 410 | border: 0px solid; 411 | border-spacing: 0px; 412 | } 413 | 414 | div.ExecutionVisualizer table.tupleTbl { 415 | border-width: 1px 1px 1px 0px; 416 | border-style: solid; 417 | border-spacing: 0px; 418 | } 419 | 420 | div.ExecutionVisualizer table.setTbl { 421 | border: 1px solid; 422 | border-spacing: 0px; 423 | text-align: center; 424 | } 425 | 426 | /** 427 | * list和tuple头。 428 | */ 429 | div.ExecutionVisualizer table.listTbl td.listHeader, 430 | div.ExecutionVisualizer table.tupleTbl td.tupleHeader { 431 | border-left: 1px solid; 432 | padding: 2px 1px 3px 4px; 433 | text-align: left; 434 | font-size: 8pt; 435 | } 436 | 437 | 438 | /** 439 | * list、tuple和set中的元素。 440 | */ 441 | div.ExecutionVisualizer table.listTbl td.listElt { 442 | border-bottom: 1px solid; 443 | border-left: 1px solid; 444 | } 445 | 446 | div.ExecutionVisualizer table.tupleTbl td.tupleElt { 447 | border-left: 1px solid; 448 | } 449 | 450 | div.ExecutionVisualizer table.listTbl td.listElt, 451 | div.ExecutionVisualizer table.tupleTbl td.tupleElt { 452 | padding: 0px 10px 8px; 453 | vertical-align: bottom; 454 | } 455 | 456 | div.ExecutionVisualizer table.setTbl td.setElt { 457 | padding: 8px 458 | } 459 | 460 | /** 461 | * 自定义对象。 462 | */ 463 | div.ExecutionVisualizer tabl.customObjTbl { 464 | border: 1px solid; 465 | } 466 | 467 | /** 468 | * 自定义对象中的元素。 469 | */ 470 | div.ExecutionVisualizer table.customObjTbl td.customObjElt { 471 | padding: 5px; 472 | } 473 | 474 | /** 475 | * dict、instance、class。 476 | */ 477 | div.ExecutionVisualizer table.dictTbl, 478 | div.ExecutionVisualizer table.instTbl, 479 | div.ExecutionVisualizer table.classTbl { 480 | border-spacing: 1px; 481 | } 482 | 483 | /** 484 | * dict、instance和class中的键。 485 | */ 486 | div.ExecutionVisualizer table.dictTbl td.dictKey, 487 | div.ExecutionVisualizer table.dictTbl td.instKey, 488 | div.ExecutionVisualizer table.classTbl td.classKey { 489 | padding: 6px 4px 6px 10px; 490 | text-align: right; 491 | } 492 | 493 | /** 494 | * dict、instance和class中的值。 495 | */ 496 | div.ExecutionVisualizer table.dictTbl td.dictVal, 497 | div.ExecutionVisualizer table.instTbl td.instVal, 498 | div.ExecutionVisualizer table.classTbl td.classVal { 499 | padding: 6px 10px 6px 4px; 500 | } 501 | 502 | /** 503 | * class和function。 504 | */ 505 | div.ExecutionVisualizer table.classTbl, 506 | div.ExecutionVisualizer table.funcTbl { 507 | border-collapse: collapse; 508 | border: 1px solid; 509 | } 510 | 511 | /** 512 | * class和instance表格下的元素。 513 | */ 514 | div.ExecutionVisualizer table.classTbl td, 515 | div.ExectuonVisualizer tabl.instTbl td { 516 | border-bottom: 1px solid; 517 | } 518 | 519 | /** 520 | * class和instance中的值。 521 | */ 522 | div.ExecutionVisualizer table.classTbl td.classVal, 523 | div.ExecutionVisualizer table.instTbl td.instVal { 524 | border-left: 1px solid; 525 | } 526 | 527 | /** 528 | * dict嵌入到list、tuple、dict、inst以及class中的样式。 529 | */ 530 | div.ExecutionVisualizer td.listElt table.dictTbl, 531 | div.ExecutionVisualizer td.tupleElt table.dictTbl, 532 | div.ExecutionVisualizer td.dictVal table.dictTbl, 533 | div.ExecutionVisualizer td.instVal table.dictTbl, 534 | div.ExecutionVisualizer td.classVal table.dictTbl { 535 | border: 1px solid; 536 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const cp = require('child_process'); 4 | const gulp = require('gulp'); 5 | const del = require('del'); 6 | const filter = require('gulp-filter'); 7 | const parseSemver = require('parse-semver'); 8 | const _ = require('underscore'); 9 | 10 | function asYarnDependency(prefix, tree) { 11 | let parseResult; 12 | 13 | try { 14 | parseResult = parseSemver(tree.name); 15 | } catch (err) { 16 | err.message += `: ${tree.name}`; 17 | console.warn(`Could not parse semver: ${tree.name}`); 18 | return null; 19 | } 20 | 21 | if (parseResult.version !== parseResult.range) { 22 | return null; 23 | } 24 | 25 | const name = parseResult.name; 26 | const version = parseResult.version; 27 | const dependencyPath = path.join(prefix, name); 28 | const children = []; 29 | 30 | for (const child of (tree.children || [])) { 31 | const dep = asYarnDependency(path.join(prefix, name, 'node_modules'), child); 32 | 33 | if (dep) { 34 | children.push(dep); 35 | } 36 | } 37 | 38 | return { name, version, path: dependencyPath, children }; 39 | } 40 | 41 | function getYarnProductionDependencies(cwd) { 42 | const raw = cp.execSync(`yarn list --json`, { cwd, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, 'ignore'] }); 43 | const match = /^{"type":"tree".*$/m.exec(raw); 44 | 45 | if (!match || match.length !== 1) { 46 | throw new Error('Could not parse result of `yarn lst --json`'); 47 | } 48 | 49 | const trees = JSON.parse(match[0]).data.trees; 50 | 51 | return trees.map(tree => asYarnDependency(path.join(cwd, 'node_modules'), tree)) 52 | .filter(dep => !!dep); 53 | } 54 | 55 | function getProductionDependencies(cwd) { 56 | const result = []; 57 | const deps = getYarnProductionDependencies(cwd); 58 | const flatten = dep => { result.push({ name: dep.name, version: dep.version, path: dep.path }); dep.children.forEach(flatten) }; 59 | deps.forEach(flatten); 60 | 61 | return _.uniq(result); 62 | } 63 | 64 | gulp.task('printDeps', () => { 65 | const dependencies = getProductionDependencies(__dirname); 66 | dependencies.map(dep => dep.path).forEach(path => console.log(path)); 67 | }); 68 | 69 | gulp.task('clean:python-preview', () => { 70 | return del(['python-preview']) 71 | }); 72 | 73 | gulp.task('extract:python-preview', ['clean:python-preview'], () => { 74 | const pythonPreviewIgnore = fs.readFileSync('./.testignore', 'utf-8') 75 | .split('\n') 76 | .map(line => line.trim()) 77 | .filter(line => line !== '') 78 | .map(line => '!' + line); 79 | const pythonPreviewFilter = filter(['**', '!node_modules/**', '!node_modules', ...pythonPreviewIgnore]); 80 | gulp.src('./**', {base: '.'}) 81 | .pipe(pythonPreviewFilter) 82 | .pipe(gulp.dest('python-preview')); 83 | 84 | const dependencies = getProductionDependencies(__dirname); 85 | const paths = dependencies.map(dep => path.join(dep.path, '**')); 86 | return gulp.src(paths, { base: './node_modules' }) 87 | .pipe(gulp.dest('./python-preview/node_modules')); 88 | }); -------------------------------------------------------------------------------- /images/previewDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/images/previewDemo.gif -------------------------------------------------------------------------------- /images/settingDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/images/settingDemo.gif -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Python Preview", 3 | "description": "Provide Preview for Python Execution.", 4 | "pythonPreview.showPreview.title": "Open Preview", 5 | "pythonPreview.showPreviewToSide.title": "Open Preview to the Side", 6 | "pythonPreview.showLockedPreviewToSide.title": "Open Locked Preview to the Side", 7 | "pythonPreview.showSource.title": "Show Source", 8 | "pythonPreview.refresh.title": "Refresh Preview", 9 | "pythonPreview.toggleLock.title": "Toggle Preview Locking", 10 | "pythonPreview.disableHeapNesting.desc": "Set how heap objects should be rendered. \"true\" render all heap objects at the top level. Otherwise, nest heap objects.", 11 | "pythonPreview.textualMemoryLabels.desc": "Render refrences using textual memory labels rather than as jsPlumb arrows.", 12 | "pythonPreview.compactFuncLabels.desc": "Render functions with a 'func' prefix and not type label.", 13 | "pythonPreview.showAllFrameLabels.desc": "Display frame and parent frame labels for all functions.", 14 | "pythonPreview.hideCode.desc": "Hide the code display.", 15 | "pythonPreview.codAndNavWidth.desc": "Set code and navigation area width.", 16 | "pythonPreview.allowAllModules.desc": "Set whether all modules can be imported.", 17 | "pythonPreview.maxExecutedLines.desc": "Set the max number of executed lines, in order to against infinite loops.", 18 | "pythonPreview.cumulativeMode.desc": "Display all stack frames that have ever exited rather than only currently on the stack.", 19 | "pythonPreview.trace.desc": "Enable debug logging for the python-preview extension.", 20 | 21 | "pythonPreview.fontFamily.desc": "Control the font family used in the python preview.", 22 | "pythonPreview.fontSize.desc": "Control the font size in pixels used in the python preview.", 23 | "pythonPreview.langDisplay.fontFamily.desc": "Control the font family used in the lang display area.", 24 | "pythonPreview.langDisplay.fontSize.desc": "Control the font size in pixels used in the lang display area.", 25 | "pythonPreview.code.fontFamily.desc": "Control the font family used in the code display area.", 26 | "pythonPreview.code.fontSize.desc": "Control the font size in pixels in the code display area.", 27 | "pythonPreview.code.lineHeight.desc": "Control the line height used in the code.", 28 | "pythonPreview.legend.fontFamily.desc": "Control the font family used in the arrow legend.", 29 | "pythonPreview.legend.fontSize.desc": "Control the font size in pixels used in the arrow legend.", 30 | "pythonPreview.codeFooterDocs.fontFamily.desc": "Control the font family used in the code footer docs.", 31 | "pythonPreview.codeFooterDocs.fontSize.desc": "Control the font size in pixels used in the code footer docs.", 32 | "pythonPreview.printOutputDocs.fontFamily.desc": "Control the font family used int the print output docs.", 33 | "pythonPreview.printOutputDocs.fontSize.desc": "Control the font size in pixels in the print output docs.", 34 | "pythonPreview.pyStdout.fontSize.desc": "Control the font size in pixels used in the python stdout window.", 35 | "pythonPreview.pyStdout.fontFamily.desc": "Control the font family used in the python stdout window.", 36 | "pythonPreview.stackAndHeapHeader.fontFamily.desc": "Control the font family used in the stack header and heap header.", 37 | "pythonPreview.stackAndHeapHeader.fontSize.desc": "Control the font size in pixels used in the stack header and heap header.", 38 | "pythonPreview.stackFrame.fontFamily.desc": "Control the font family used in the stack frame.", 39 | "pythonPreview.stackFrame.fontSize.desc": "Control the font size in pixels used in the stack frame.", 40 | "pythonPreview.retVal.fontSize.desc": "Control the font size in pixels used in the function return value.", 41 | "pythonPreview.stackFrameHeader.fontFamily.desc": "Control the font family used in the stack frame header.", 42 | "pythonPreview.stackFrameHeader.fontSize.desc": "Control the font size in pixels used in the stack frame header.", 43 | "pythonPreview.heapObject.fontFamily.desc": "Control the font family used in the heap object.", 44 | "pythonPreview.heapObject.fontSize.desc": "Control the font size in pixels used in the heap object.", 45 | "pythonPreview.typeLabel.fontFamily.desc": "Control the font family used in the type label of heap object.", 46 | "pythonPreview.typeLabel.fontSize.desc": "Control the font size in pixels used in the type label of heap object.", 47 | 48 | "pythonPreview.light.highlightedArrow.color.desc": "Control the color used in highlighted arrow in vscode light theme.", 49 | "pythonPreview.light.highlightedStackFrame.bgColor.desc": "Control the background color used in highlighted stack frame in vscode light theme.", 50 | "pythonPreview.light.list-tuple-setTbl.bgColor.desc": "Control the background color used in list, tuple and set in vscode light theme.", 51 | "pythonPreview.light.dict-class-instKey.bgColor.desc": "Control the background color used in dict, class and instance key in vscode light theme.", 52 | "pythonPreview.light.dict-class-instVal.bgColor.desc": "Control the background color used in dict, class and instance value in vscode light theme.", 53 | "pythonPreview.dark.highlightedArrow.color.desc": "Control the color used in highlighted arrow in vscode dark theme.", 54 | "pythonPreview.dark.highlightedStackFrame.bgColor.desc": "Control the background color used in highlighted stack frame in vscode dark theme.", 55 | "pythonPreview.dark.list-tuple-setTbl.bgColor.desc": "Control the background color used in list, tuple and set in vscode dark theme.", 56 | "pythonPreview.dark.dict-class-instKey.bgColor.desc": "Control the background color used in dict, class and instance key in vscode dark theme.", 57 | "pythonPreview.dark.dict-class-instVal.bgColor.desc": "Control the background color used in dict, class and instance value in vscode dark theme.", 58 | "pythonPreview.high-contrast.highlightedArrow.color.desc": "Control the color used in highlighted stack frame in vscode high-contrast theme.", 59 | "pythonPreview.high-contrast.highlightedStackFrame.bgColor.desc": "Control the background color used in highlighted stack frame in vscode high-contrast theme.", 60 | "pythonPreview.high-contrast.list-tuple-setTbl.bgColor.desc": "Control the background color used in list, tuple and set in vscode high-contrast theme.", 61 | "pythonPreview.high-contrast.dict-class-instKey.bgColor.desc": "Control the background color used in dict, class and instance key in vscode high-contrast theme.", 62 | "pythonPreview.high-contrast.dict-class-instVal.bgColor.desc": "Control the background color used in dict, class and instance value in vscode high-contrast theme." 63 | } -------------------------------------------------------------------------------- /package.nls.zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Python 预览", 3 | "description": "为 Python 执行提供预览效果。", 4 | "pythonPreview.showPreview.title": "打开预览", 5 | "pythonPreview.showPreviewToSide.title": "在侧边打开预览", 6 | "pythonPreview.showLockedPreviewToSide.title": "在侧边打开锁定的预览", 7 | "pythonPreview.showSource.title": "显示源", 8 | "pythonPreview.refresh.title": "刷新预览", 9 | "pythonPreview.toggleLock.title": "切换预览的锁定状态", 10 | "pythonPreview.disableHeapNesting.desc": "设置堆对象的渲染方式。\"true\" 在顶层渲染所有的堆对象。否则, 嵌套堆对象。", 11 | "pythonPreview.textualMemoryLabels.desc": "使用文本内存标签而不是jsPlumb箭头渲染引用。", 12 | "pythonPreview.compactFuncLabels.desc": "使用 \"func\" 前缀渲染函数类型标签。", 13 | "pythonPreview.showAllFrameLabels.desc": "显示所有函数的栈帧以及父栈帧标签。", 14 | "pythonPreview.hideCode.desc": "隐藏代码显示。", 15 | "pythonPreview.codAndNavWidth": "设置代码和导航区域宽度。", 16 | "pythonPreview.allowAllModules.desc": "设置是否可以导入所有模块。", 17 | "pythonPreview.maxExecutedLines.desc": "设置执行行数的最大值,以防无限的循环。", 18 | "pythonPreview.cumulativeMode.desc": "显示所有已退出的栈帧,而不仅是当前栈帧。", 19 | "pythonPreview.trace.desc": "对 python-preview 启用调试日志记录。", 20 | 21 | "pythonPreview.fontFamily.desc": "控制 python 预览中使用的字体系列。", 22 | "pythonPreview.fontSize.desc": "控制 python 预览中使用的字号(以像素为单位)。", 23 | "pythonPreview.langDisplay.fontFamily.desc": "控制语言显示区域中使用的字体系列。", 24 | "pythonPreview.langDisplay.fontSize.desc": "控制语言显示区域中使用的字号(以像素为单位)。", 25 | "pythonPreview.code.fontFamily.desc": "控制代码显示区域中使用的字体系列。", 26 | "pythonPreview.code.fontSize.desc": "控制代码显示区域中使用的字号(以像素为单位)。", 27 | "pythonPreview.code.lineHeight.desc": "控制代码显示区域中使用的行高。", 28 | "pythonPreview.legend.fontFamily.desc": "控制箭头图例中使用的字体系列。", 29 | "pythonPreview.legend.fontSize.desc": "控制箭头图例中使用的字号(以像素为单位)。", 30 | "pythonPreview.codeFooterDocs.fontFamily.desc": "控制代码页脚文档中使用的字体系列。", 31 | "pythonPreview.codeFooterDocs.fontSize.desc": "控制代码页脚文档中使用的字号(以像素为单位)。", 32 | "pythonPreview.printOutputDocs.fontFamily.desc": "控制打印输出文档中使用的字体系列。", 33 | "pythonPreview.printOutoutDocs.fontSize.desc": "控制打印输出文档中使用的字号(以像素為单位)。", 34 | "pythonPreview.pyStdout.fontFamily.desc": "控制 python 标准输出窗口中使用的字体系列。", 35 | "pythonPreview.pyStdout.fontSize.desc": "控制 python 标准输出窗口中使用的字号(以像素为单位)。", 36 | "pythonPreview.stackAndHeapHeader.fontFamily.desc": "控制栈头部和堆头部中使用的字体系列。", 37 | "pythonPreview.stackAndHeapHeader.fontSize.desc": "控制栈头部和堆头部中使用的字号(以像素为单位)。", 38 | "pythonPreview.stackFrame.fontFamily.desc": "控制栈帧中使用的字体系列。", 39 | "pythonPreview.stackFrame.fontSize.desc": "控制栈帧中使用的字号(以像素为单位)。", 40 | "pythonPreview.retVal.fontSize.desc": "控制函数返回值中使用的字号(以像素为单位)。", 41 | "pythonPreview.stackFrameHeader.fontFamily.desc": "控制栈帧头部中使用的字体系列。", 42 | "pythonPreview.stackFrameHeader.fontSize.desc": "控制栈帧头部中使用的字号(以像素为单位)。", 43 | "pythonPreview.heapObject.fontFamily.desc": "控制堆对象中使用的字体系列。", 44 | "pythonPreview.heapObject.fontSize.desc": "控制堆对象中使用的字号(以像素为单位)。", 45 | "pythonPreview.typeLabel.fontFamily.desc": "控制堆对象的类型标签中使用的字体系列。", 46 | "pythonPreview.typeLabel.fontSize.desc": "控制堆对象的类型标签中使用的字号(以像素为单位)。", 47 | 48 | "pythonPreview.light.highlightedArrow.color.desc": "控制 vscode 浅色主题下高亮箭头中使用的颜色。", 49 | "pythonPreview.light.highlightedStackFrame.bgColor.desc": "控制 vscode 浅色主题中高亮堆栈使用的背景色。", 50 | "pythonPreview.light.list-tuple-setTbl.bgColor.desc": "控制 vscode 浅色主题下列表、元祖和集合使用的背景色。", 51 | "pythonPreview.light.dict-class-instKey.bgColor.desc": "控制 vscode 浅色主题下字典、类和实例中键使用的背景色。", 52 | "pythonPreview.light.dict-class-instVal.bgColor.desc": "控制 vscode 浅色主题下字典、类和实例中值使用的背景色。", 53 | "pythonPreview.dark.highlightedArrow.color.desc": "控制 vscode 深色主题下高亮箭头中使用的颜色。", 54 | "pythonPreview.dark.highlightedStackFrame.bgColor.desc": "控制 vscode 深色主题中高亮堆栈使用的背景色。", 55 | "pythonPreview.dark.list-tuple-setTbl.bgColor.desc": "控制 vscode 深色主题下列表、元祖和集合使用的背景色。", 56 | "pythonPreview.dark.dict-class-instKey.bgColor.desc": "控制 vscode 深色主题下字典、类和实例中键使用的背景色。", 57 | "pythonPreview.dark.dict-class-instVal.bgColor.desc": "控制 vscode 深色主题下字典、类和实例中值使用的背景色。", 58 | "pythonPreview.high-contrast.highlightedArrow.color.desc": "控制 vscode 高对比度主题下高亮箭头中使用的颜色。", 59 | "pythonPreview.high-contrast.highlightedStackFrame.bgColor.desc": "控制 vscode 高对比度主题中高亮堆栈使用的背景色。", 60 | "pythonPreview.high-contrast.list-tuple-setTbl.bgColor.desc": "控制 vscode 高对比度主题下列表、元祖和集合使用的背景色。", 61 | "pythonPreview.high-contrast.dict-class-instKey.bgColor.desc": "控制 vscode 高对比度主题下字典、类和实例中键使用的背景色。", 62 | "pythonPreview.high-contrast.dict-class-instVal.bgColor.desc": "控制 vscode 高对比度主题下字典、类和实例中值使用的背景色。" 63 | } -------------------------------------------------------------------------------- /package.nls.zh-tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Python 預覽", 3 | "description": "为 Python 執行提供預覽效果。", 4 | "pythonPreview.showPreview.title": "打開預覽。", 5 | "pythonPreview.showPreviewToSide.title": "在側邊打開預覽", 6 | "pythonPreview.showLockedPreviewToSide.title": "在側邊打開鎖定的預覽", 7 | "pythonPreview.showSource.title": "顯示源", 8 | "pythonPreview.refresh.title": "刷新預覽", 9 | "pythonPreview.toggleLock.title": "切換預覽的鎖定狀態", 10 | "pythonPreview.disableHeapNesting.desc": "設置堆對象的渲染方式。\"true\" 在頂層渲染所有的堆對象。否則,嵌套對對象。", 11 | "pythonPreview.textualMemoryLabels.desc": "使用文本內存標籤而不是jsPlumb箭頭渲染引用。", 12 | "pythonPreview.compactFuncLabels.desc": "使用 \"func\" 前綴渲染函數類型標籤。", 13 | "pythonPreview.showAllFrameLabels.desc": "顯示所有函數的棧幀以及父棧幀標籤。", 14 | "pythonPreview.hideCode.desc": "隱藏代碼顯示。", 15 | "pythonPreview.codAndNavWidth": "設置代碼和導航區域寬度。", 16 | "pythonPreview.allowAllModules.desc": "設置是否可以導入所有模塊。", 17 | "pythonPreview.maxExecutedLines.desc": "設置執行行數的最大值,以防無線的循環。", 18 | "pythonPreview.cumulativeMode.desc": "顯示所有已退出的棧幀,而不僅是當前堆棧。", 19 | "pythonPreview.trace.desc": "允許 python-preview 拓展啟用調試日誌記錄。", 20 | 21 | "pythonPreview.fontFamily.desc": "控制 python 預覽中使用的字體系列。", 22 | "pythonPreview.fontSize.desc": "控制 python 預覽中使用的字號(以像素為單位)。", 23 | "pythonPreview.langDisplay.fontFamily.desc": "控制語言顯示區域中使用的字體系列。", 24 | "pythonPreview.langDisplay.fontSize.desc": "控制語言顯示區域中使用的字號(以像素為單位)。", 25 | "pythonPreview.code.fontFamily.desc": "控制代碼顯示區域中使用的字體系列。", 26 | "pythonPreview.code.fontSize.desc": "控制代碼顯示區域中使用的字號(以像素為單位)。", 27 | "pythonPreview.code.lineHeight.desc": "控制代碼顯示區域中使用的行高。", 28 | "pythonPreview.legend.fontFamily.desc": "控制箭頭圖列中使用的字體系列。", 29 | "pythonPreview.legend.fontSize.desc": "控制箭頭圖列中使用的字號(以像素為單位)。", 30 | "pythonPreview.codeFooterDocs.fontFamily.desc": "控制代碼頁腳文檔中使用的字體系列。", 31 | "pythonPreview.codeFooterDocs.fontSize.desc": "控制代碼頁腳文檔中使用的字號(以像素為單位)。", 32 | "pythonPreview.printOutputDocs.fontFamily.desc": "控制打印輸出文檔中使用的字體系列。", 33 | "pythonPreview.printOutoutDocs.fontSize.desc": "控制打印輸出文檔中使用的字號(以像素為單位)。", 34 | "pythonPreview.pyStdout.fontFamily.desc": "控制 python 標準輸出窗口中使用的字體系列。", 35 | "pythonPreview.pyStdout.fontSize.desc": "控制 python 標準輸出窗口中使用的字號(以像素為單位)。", 36 | "pythonPreview.stackAndHeapHeader.fontFamily.desc": "控制棧頭部和堆頭部中使用的字體系列。", 37 | "pythonPreview.stackAndHeapHeader.fontSize.desc": "控制棧頭部和堆頭部中使用的字號(以像素為單位)。", 38 | "pythonPreview.stackFrame.fontFamily.desc": "控制棧幀中使用的字體系列。", 39 | "pythonPreview.stackFrame.fontSize.desc": "控制棧幀中使用的字號(以像素為單位)。", 40 | "pythonPreview.retVal.fontSize.desc": "控制函數返回值中使用的字號(以像素為單位)。", 41 | "pythonPreview.stackFrameHeader.fontFamily.desc": "控制棧幀頭部中使用的字體系列。", 42 | "pythonPreview.stackFrameHeader.fontSize.desc": "控制棧幀頭部中使用的字號(以像素為單位)。", 43 | "pythonPreview.heapObject.fontFamily.desc": "控制堆對象中使用的字體系列。", 44 | "pythonPreview.heapObject.fontSize.desc": "控制堆對象中使用的字號(以像素為單位)。", 45 | "pythonPreview.typeLabel.fontFamily.desc": "控制堆對象的類型標籤中使用的字體系列。", 46 | "pythonPreview.typeLabel.fontSize.desc": "控制堆對象的類型標籤中使用的字號(以像素為單位)。", 47 | 48 | "pythonPreview.light.highlightedArrow.color.desc": "控制 vscode 淺色主題下高亮箭頭使用的顏色。", 49 | "pythonPreview.light.highlightedStackFrame.bgColor.desc": "控制 vscode 淺色主題下高亮堆棧使用的背景色。", 50 | "pythonPreview.light.list-tuple-setTbl.bgColor.desc": "控制 vscode 淺色主題下列表、元祖和集合使用的背景色。", 51 | "pythonPreview.light.dict-class-instKey.bgColor.desc": "控制 vscode 淺色主題下字典、類和實例中鍵使用的背景色。", 52 | "pythonPreview.light.dict-class-instVal.bgColor.desc": "控制 vscode 淺色主題下字典、類和實例中值使用的背景色。", 53 | "pythonPreview.dark.highlightedArrow.color.desc": "控制 vscode 深色主題下高亮箭頭使用的顏色。", 54 | "pythonPreview.dark.highlightedStackFrame.bgColor.desc": "控制 vscode 深色主題下高亮堆棧使用的背景色。", 55 | "pythonPreview.dark.list-tuple-setTbl.bgColor.desc": "控制 vscode 深色主題下列表、元祖和集合使用的背景色。", 56 | "pythonPreview.dark.dict-class-instKey.bgColor.desc": "控制 vscode 深色主題下字典、類和實例中鍵使用的背景色。", 57 | "pythonPreview.dark.dict-class-instVal.bgColor.desc": "控制 vscode 深色主題下字典、類和實例中值使用的背景色。", 58 | "pythonPreview.high-contrast.highlightedArrow.color.desc": "控制 vscode 高對比度主題下高亮箭頭使用的顏色。", 59 | "pythonPreview.high-contrast.highlightedStackFrame.bgColor.desc": "控制 vscode 高對比度主題下高亮堆棧使用的背景色。", 60 | "pythonPreview.high-contrast.list-tuple-setTbl.bgColor.desc": "控制 vscode 高對比度主題下列表、元祖和集合使用的背景色。", 61 | "pythonPreview.high-contrast.dict-class-instKey.bgColor.desc": "控制 vscode 高對比度主題下字典、類和實例中鍵使用的背景色。", 62 | "pythonPreview.high-contrast.dict-class-instVal.bgColor.desc": "控制 vscode 高對比度主題下字典、類和實例中值使用的背景色。" 63 | } -------------------------------------------------------------------------------- /preview-src/README.md: -------------------------------------------------------------------------------- 1 | For developing using TypeScript: 2 | 1. run 'npm install typings -g'. 3 | 2. run 'typings install' (to install definition moduels saved in typings.json). -------------------------------------------------------------------------------- /preview-src/datas.ts: -------------------------------------------------------------------------------- 1 | export interface PreviewState { 2 | resource: string, 3 | locked: boolean, 4 | startingInstruction: number, 5 | width: number 6 | } 7 | 8 | export function getDataState(): PreviewState { 9 | const element = document.getElementById('vscode-python-preview-data'); 10 | if (element) { 11 | const dataState = element.getAttribute('data-state') 12 | if (dataState) { 13 | return JSON.parse(dataState); 14 | } 15 | } 16 | 17 | throw new Error(`Could not load data state`); 18 | } -------------------------------------------------------------------------------- /preview-src/events.ts: -------------------------------------------------------------------------------- 1 | export function onceDocumentLoaded(f: () => void) { 2 | if (document.readyState === 'loading') { 3 | document.addEventListener('DOMContentLoaded', f); 4 | } else { 5 | f(); 6 | } 7 | } -------------------------------------------------------------------------------- /preview-src/index.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionVisualizer } from "./pytutor"; 2 | import { getDataState } from "./datas"; 3 | import { createPosterForVsCode } from "./messaging"; 4 | 5 | declare var acquireVsCodeApi: any; 6 | 7 | const vscode = acquireVsCodeApi(); 8 | 9 | // 设置vscode状态 10 | const state = getDataState(); 11 | vscode.setState(state); 12 | 13 | const messagePoster = createPosterForVsCode(vscode); 14 | let pyOutputPane: ExecutionVisualizer | undefined; 15 | 16 | 17 | window.addEventListener('message', event => { 18 | switch(event.data.type) { 19 | case 'updateContent': 20 | event.data.options.updateOutputCallback = visualizer => { 21 | state.startingInstruction = visualizer.curInstr; 22 | vscode.setState(state); 23 | messagePoster.postMessage('updateStartingInstruction', { curInstr: visualizer.curInstr }); 24 | } 25 | pyOutputPane = new ExecutionVisualizer('pyOutputPane', event.data.data, event.data.options); 26 | pyOutputPane.redrawConnectors(); 27 | break; 28 | case 'updateLock': 29 | state.locked = event.data.locked; 30 | vscode.setState(state); 31 | break; 32 | } 33 | }); 34 | 35 | $(window).resize(() => { 36 | if (pyOutputPane) { 37 | pyOutputPane.redrawConnectors(); 38 | let width = document.getElementById('codAndNav').style['width']; 39 | state.width = parseFloat(width.slice(0, -2)); 40 | messagePoster.postMessage('updateCodAndNavWidth', { width: state.width }); 41 | } 42 | }); -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/preview-src/lib/jquery-ui-1.11.4/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/jquery-ui.structure.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.4 - 2015-03-11 2 | * http://jqueryui.com 3 | * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:0.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:0.4em 2.1em 0.4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px} -------------------------------------------------------------------------------- /preview-src/lib/jquery-ui-1.11.4/jquery-ui.theme.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.4 - 2015-03-11 2 | * http://jqueryui.com 3 | * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);border-radius:5px} -------------------------------------------------------------------------------- /preview-src/messaging.ts: -------------------------------------------------------------------------------- 1 | import { getDataState } from "./datas"; 2 | 3 | export interface MessagePoster { 4 | postMessage(type: string, body: object): void; 5 | 6 | postCommand(command: string, args: any[]): void; 7 | } 8 | 9 | export const createPosterForVsCode = (vscode: any) => { 10 | return new class implements MessagePoster { 11 | postMessage(type: string, body: object): void { 12 | vscode.postMessage({ 13 | type: type, 14 | source: getDataState().resource, 15 | body: body 16 | }); 17 | } 18 | 19 | postCommand(command: string, args: any[]) { 20 | this.postMessage('command', { command, args }); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /preview-src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preview-src", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "install-lib-typings": "typings install", 8 | "dev": "webpack --progress --colors --config webpack.dev.js", 9 | "prod": "webpack --progress --colors --config webpack.prod.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "BSD-3-Clause", 14 | "devDependencies": { 15 | "clean-webpack-plugin": "^0.1.19", 16 | "ts-loader": "^3.5.0", 17 | "typescript": "^2.8.3", 18 | "uglifyjs-webpack-plugin": "^1.3.0", 19 | "webpack": "^3.11.0", 20 | "webpack-merge": "^4.1.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /preview-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "sourceMap": true 6 | }, 7 | "exclude": [ 8 | "node_modules" 9 | ] 10 | } -------------------------------------------------------------------------------- /preview-src/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preview-src", 3 | "globalDependencies": { 4 | "require": "github:DefinitelyTyped/DefinitelyTyped/requirejs/require.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 5 | "jquery": "github:DefinitelyTyped/DefinitelyTyped/jquery/jquery.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 6 | "jquery.bbq": "github:DefinitelyTyped/DefinitelyTyped/jquery.bbq/jquery.bbq.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 7 | "ace": "github:DefinitelyTyped/DefinitelyTyped/ace/ace.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 8 | "qtip2": "github:DefinitelyTyped/DefinitelyTyped/qtip2/qtip2.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 9 | "jqueryui": "github:DefinitelyTyped/DefinitelyTyped/jqueryui/jqueryui.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 10 | "d3": "github:DefinitelyTyped/DefinitelyTyped/d3/d3.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 11 | "diff-match-patch": "github:DefinitelyTyped/DefinitelyTyped/diff-match-patch/diff-match-patch.d.ts#1315d46f8066eec8c197914da88e98629ebc2254", 12 | "jquery.simplemodal": "github:DefinitelyTyped/DefinitelyTyped/jquery.simplemodal/jquery.simplemodal.d.ts#1315d46f8066eec8c197914da88e98629ebc2254" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /preview-src/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: './index.ts', 6 | 7 | output: { 8 | path: path.resolve(__dirname, '..', 'assets'), 9 | filename: 'index.js' 10 | }, 11 | 12 | module: { 13 | loaders: [ 14 | { 15 | test: /\.ts$/, 16 | loader: 'ts-loader' 17 | } 18 | ] 19 | }, 20 | 21 | resolve: { 22 | extensions: ['.ts', '.js'], 23 | alias: { 24 | "jquery": path.resolve(__dirname, 'lib', 'jquery-3.0.0.min.js'), 25 | "$": path.resolve(__dirname, 'lib', 'jquery-3.0.0.min.js'), 26 | "$.bbq": path.resolve(__dirname, 'lib', 'jquery.ba-bbq.js') 27 | } 28 | }, 29 | 30 | plugins: [ 31 | new webpack.ProvidePlugin({ 32 | jquery: "jquery", 33 | jQuery: "jquery", 34 | $: 'jquery' 35 | }) 36 | ] 37 | } -------------------------------------------------------------------------------- /preview-src/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | // mode: 'development', 6 | devtool: 'inline-source-map' 7 | }) -------------------------------------------------------------------------------- /preview-src/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 4 | 5 | module.exports = merge(common, { 6 | plugins: [ 7 | new UglifyJsPlugin({ 8 | uglifyOptions: { 9 | warnings: false, 10 | output: { 11 | comments: false 12 | } 13 | } 14 | }) 15 | ] 16 | }) -------------------------------------------------------------------------------- /python-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongli0x00/python-preview/f574a46aad9a0483fcfd1ad331405cce6e15b8d9/python-icon.png -------------------------------------------------------------------------------- /pythonFiles/pydev/debugger.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | import traceback 4 | import pg_logger 5 | import json 6 | 7 | import util as _util 8 | 9 | to_bytes = _util.to_bytes 10 | to_str = _util.to_str 11 | read_bytes = _util.read_bytes 12 | write_bytes = _util.write_bytes 13 | read_int = _util.read_int 14 | write_int = _util.write_int 15 | read_string = _util.read_string 16 | write_string = _util.write_string 17 | 18 | try: 19 | xrange 20 | except: 21 | xrange = range 22 | 23 | LOAD = to_bytes('LOAD') 24 | OUTP = to_bytes('OUTP') 25 | DETC = to_bytes('DETC') 26 | 27 | def debug(port_num, debug_id, current_pid): 28 | attach_process(port_num, debug_id, current_pid) 29 | 30 | report_process_loaded() 31 | 32 | DebuggerLoop(conn).loop() 33 | 34 | def report_process_loaded(): 35 | write_bytes(conn, LOAD) 36 | write_string(conn, '.'.join(map(str, sys.version_info))) 37 | 38 | def attach_process(port_num, debug_id, current_pid): 39 | global conn 40 | for i in xrange(50): 41 | try: 42 | conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 43 | conn.connect(('127.0.0.1', port_num)) 44 | write_string(conn, debug_id) 45 | write_int(conn, 0) # success 46 | write_int(conn, current_pid) # success 47 | break 48 | except: 49 | import time 50 | time.sleep(50. / 1000) 51 | else: 52 | sys.stdout.write('&error&failed to attach') 53 | sys.stderr.flush() 54 | raise Exception('failed to attach') 55 | 56 | 57 | class DebuggerExitException(Exception): pass 58 | 59 | 60 | class DebuggerLoop: 61 | instacne = None 62 | 63 | def __init__(self, conn): 64 | DebuggerLoop.instacne = self 65 | self._conn = conn 66 | self._command_table = { 67 | to_bytes('outp'): self.command_exec_script, 68 | to_bytes('detc'): self.command_detach 69 | } 70 | 71 | def loop(self): 72 | try: 73 | while True: 74 | index = read_bytes(self._conn, 4) 75 | cmd = self._command_table.get(index) 76 | if cmd is not None: 77 | cmd() 78 | else: 79 | if index: 80 | sys.stdout.write('&warn&unknown command: %s' % to_str(index)) 81 | sys.stdout.flush() 82 | print('unknown command', index) 83 | break 84 | except DebuggerExitException: 85 | pass 86 | except socket.error: 87 | pass 88 | except: 89 | traceback.print_exc() 90 | 91 | def command_exec_script(self): 92 | raw_input_lst_json = False 93 | heap_primitives = False 94 | cumulative_mode = read_int(self._conn) 95 | if cumulative_mode == 0: 96 | cumulative_mode = False 97 | else: 98 | cumulative_mode = True 99 | allow_all_modules = read_int(self._conn) 100 | if allow_all_modules == 0: 101 | allow_all_modules = False 102 | else: 103 | allow_all_modules = True 104 | max_executed_lines = read_int(self._conn) 105 | folder = read_string(self._conn) 106 | sys.path[0] = folder 107 | resource = read_string(self._conn) 108 | script_str = read_string(self._conn) 109 | trace_str = pg_logger.exec_script_str_local(script_str, raw_input_lst_json, cumulative_mode, heap_primitives, max_executed_lines, json_finalizer, probe_exprs=None, allow_all_modules=allow_all_modules) 110 | write_bytes(self._conn, OUTP) 111 | write_string(self._conn, resource) 112 | write_string(self._conn, trace_str) 113 | 114 | def command_detach(self): 115 | write_bytes(self._conn, DETC) 116 | 117 | 118 | def json_finalizer(input_code, output_trace): 119 | ret = dict(code=input_code, trace=output_trace) 120 | json_output = json.dumps(ret, indent=0) 121 | return json_output -------------------------------------------------------------------------------- /pythonFiles/pydev/launcher.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import sys 4 | import traceback 5 | sys.stdout.write('&info&succeeded to launch script') 6 | sys.stdout.flush() 7 | try: 8 | import debugger 9 | except: 10 | traceback.print_exc() 11 | print('Press Enter to close...') 12 | try: 13 | raw_input() 14 | except NameError: 15 | input() 16 | sys.exit(1) 17 | 18 | 19 | #======================================================================================================================= 20 | # 1. Debugger port to connect to. 21 | # 2. GUID for the debug session. 22 | # 3. Startup script name. 23 | #======================================================================================================================= 24 | port_num = int(sys.argv[1]) 25 | debug_id = sys.argv[2] 26 | del sys.argv[1:3] 27 | 28 | # filename = sys.argv[0] 29 | 30 | sys.path[0] = '' 31 | 32 | current_pid = os.getpid() 33 | 34 | del sys, os 35 | 36 | print(current_pid) 37 | 38 | debugger.debug(port_num, debug_id, current_pid) -------------------------------------------------------------------------------- /pythonFiles/pydev/util.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | from encodings import utf_8, ascii 4 | 5 | try: 6 | unicode 7 | except: 8 | unicode = str 9 | 10 | try: 11 | xrange 12 | except: 13 | xrange = range 14 | 15 | if sys.version_info[0] >= 3: 16 | def to_bytes(cmd_str): 17 | return ascii.Codec.encode(cmd_str)[0] 18 | else: 19 | def to_bytes(cmd_str): 20 | return cmd_str 21 | 22 | if sys.version_info[0] >= 3: 23 | def to_str(cmd_bytes): 24 | return ascii.Codec.decode(cmd_bytes)[0] 25 | else: 26 | def to_str(cmd_bytes): 27 | return cmd_bytes 28 | 29 | 30 | UNICODE_PREFIX = to_bytes('U') 31 | ASCII_PREFIX = to_bytes('A') 32 | NONE_PREFIX = to_bytes('N') 33 | 34 | 35 | def read_bytes(conn, count): 36 | b = to_bytes('') 37 | while len(b) < count: 38 | received_data = conn.recv(count - len(b)) 39 | if received_data is None: 40 | break 41 | b += received_data 42 | return b 43 | 44 | 45 | def write_bytes(conn, b): 46 | conn.sendall(b) 47 | 48 | 49 | def read_int(conn): 50 | # '!' represents network(=big-endian) byte order 51 | # 'q' represent long long in c type, integer in python type, 8 standard size 52 | return struct.unpack('!q', read_bytes(conn, 8))[0] 53 | 54 | 55 | def write_int(conn, i): 56 | write_bytes(conn, struct.pack('!q', i)) 57 | 58 | 59 | def read_string(conn): 60 | str_len = read_int(conn) 61 | if not str_len: 62 | return '' 63 | res = to_bytes('') 64 | while len(res) < str_len: 65 | res = res + conn.recv(str_len - len(res)) 66 | res = utf_8.decode(res)[0] 67 | if sys.version_info[0] == 2: 68 | try: 69 | res = ascii.Codec.encode(res)[0] 70 | except UnicodeEncodeError: 71 | pass 72 | return res 73 | 74 | 75 | def write_string(conn, s): 76 | if s is None: 77 | write_bytes(conn, NONE_PREFIX) 78 | elif isinstance(s, unicode): 79 | b = utf_8.encode(s)[0] 80 | b_len = len(b) 81 | write_bytes(conn, UNICODE_PREFIX) 82 | write_int(conn, b_len) 83 | if b_len > 0: 84 | write_bytes(conn, b) 85 | else: 86 | s_len = len(s) 87 | write_bytes(conn, ASCII_PREFIX) 88 | write_int(conn, s_len) 89 | if s_len > 0: 90 | write_bytes(conn, s) -------------------------------------------------------------------------------- /src/commands/index.ts: -------------------------------------------------------------------------------- 1 | export { ShowPreviewCommand, ShowPreviewToSideCommand, ShowLockedPreviewToSideCommand } from './showPreview'; 2 | export { ShowSourceCommand } from './showSource'; 3 | export { RefreshPreviewCommand } from './refreshPreview'; 4 | export { ToggleLockCommand } from './toggleLock'; -------------------------------------------------------------------------------- /src/commands/refreshPreview.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "../common/commandManager"; 2 | import { PythonPreviewManager } from "../features/previewManager"; 3 | 4 | export class RefreshPreviewCommand implements Command { 5 | public readonly id = 'pythonPreview.refresh'; 6 | 7 | public constructor(private readonly _previewManager: PythonPreviewManager) { } 8 | 9 | public execute() { 10 | this._previewManager.refresh(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/commands/showPreview.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import {Command} from "../common/commandManager"; 3 | import {PythonPreviewManager} from "../features/previewManager"; 4 | 5 | interface ShowPreviewSettings { 6 | readonly sideBySide: boolean; 7 | readonly locked: boolean; 8 | } 9 | 10 | async function showPreview(previewManager: PythonPreviewManager, 11 | uri: vscode.Uri | undefined, 12 | previewSettings: ShowPreviewSettings): Promise { 13 | let resource = uri; 14 | if (!(resource instanceof vscode.Uri)) { 15 | if (vscode.window.activeTextEditor) { 16 | resource = vscode.window.activeTextEditor.document.uri; 17 | } else { 18 | return vscode.commands.executeCommand('pythonPreview.showSource'); 19 | } 20 | } 21 | 22 | previewManager.preview(resource, { 23 | resourceColumn: (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One, 24 | previewColumn: previewSettings.sideBySide ? vscode.ViewColumn.Beside : vscode.ViewColumn.Active, 25 | locked: previewSettings.locked 26 | }); 27 | } 28 | 29 | /** 30 | * 31 | */ 32 | export class ShowPreviewCommand implements Command { 33 | public readonly id = 'pythonPreview.showPreview'; 34 | 35 | public constructor(private readonly _prviewManager: PythonPreviewManager) { } 36 | 37 | public execute() { 38 | showPreview(this._prviewManager, undefined, { 39 | sideBySide: false, 40 | locked: false 41 | }); 42 | } 43 | } 44 | 45 | 46 | export class ShowPreviewToSideCommand implements Command { 47 | public readonly id = 'pythonPreview.showPreviewToSide'; 48 | 49 | public constructor(private readonly _previewManager: PythonPreviewManager) { } 50 | 51 | public execute() { 52 | showPreview(this._previewManager, undefined, { 53 | sideBySide: true, 54 | locked: false 55 | }); 56 | } 57 | } 58 | 59 | export class ShowLockedPreviewToSideCommand implements Command { 60 | public readonly id = 'pythonPreview.showLockedPreviewToSide'; 61 | 62 | public constructor(private readonly _previewManager: PythonPreviewManager) { } 63 | 64 | public execute() { 65 | showPreview(this._previewManager, undefined, { 66 | sideBySide: true, 67 | locked: true 68 | }); 69 | } 70 | } -------------------------------------------------------------------------------- /src/commands/showSource.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import {Command} from "../common/commandManager"; 3 | import {PythonPreviewManager} from "../features/previewManager"; 4 | 5 | export class ShowSourceCommand implements Command { 6 | public readonly id = 'pythonPreview.showSource'; 7 | 8 | public constructor(private readonly _previewManager: PythonPreviewManager) { 9 | 10 | } 11 | 12 | public execute() { 13 | if (this._previewManager.activePreviewResource) { 14 | return vscode.workspace.openTextDocument(this._previewManager.activePreviewResource) 15 | .then(document => vscode.window.showTextDocument(document)); 16 | } 17 | return undefined; 18 | } 19 | } -------------------------------------------------------------------------------- /src/commands/toggleLock.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "../common/commandManager"; 2 | import { PythonPreviewManager } from "../features/previewManager"; 3 | 4 | export class ToggleLockCommand implements Command { 5 | public readonly id = 'pythonPreview.toggleLock'; 6 | 7 | public constructor(private readonly _previewManager: PythonPreviewManager) { 8 | 9 | } 10 | 11 | public execute() { 12 | this._previewManager.toggleLock(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/common/commandManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export interface Command { 4 | readonly id: string; 5 | 6 | execute(...args: any[]): void; 7 | } 8 | 9 | export class CommandManager { 10 | private readonly _commands = new Map(); 11 | 12 | public dispose() { 13 | for (const registration of this._commands.values()) { 14 | registration.dispose(); 15 | } 16 | this._commands.clear(); 17 | } 18 | 19 | public register(command: T): T { 20 | this.registerCommand(command.id, command.execute, command); 21 | return command; 22 | } 23 | 24 | private registerCommand(id: string, impl: (...args: any[]) => void, thisArg?: any) { 25 | if (this._commands.has(id)) { 26 | return; 27 | } 28 | 29 | this._commands.set(id, vscode.commands.registerCommand(id, impl, thisArg)); 30 | } 31 | } -------------------------------------------------------------------------------- /src/common/dispose.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export function disposeAll(disposables: vscode.Disposable[]) { 4 | while (disposables.length) { 5 | const item = disposables.pop(); 6 | if (item) { 7 | item.dispose(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/common/file.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export function isPythonFile(document: vscode.TextDocument): boolean { 4 | return document.languageId === 'python'; 5 | } -------------------------------------------------------------------------------- /src/common/helpers.ts: -------------------------------------------------------------------------------- 1 | export function isNotInstalledError(error: Error): boolean { 2 | const isError = typeof(error) === 'object' && error != null; 3 | const errorObj = error; 4 | if (!isError) { 5 | return false; 6 | } 7 | const isModuleNotInstalledError = error.message.indexOf('No module named') >= 0; 8 | return errorObj.code === 'ENOENT' || errorObj.code === 127 || isModuleNotInstalledError; 9 | } 10 | 11 | export interface Deferred { 12 | readonly promise: Promise; 13 | readonly resolved: boolean; 14 | readonly rejected: boolean; 15 | readonly completed: boolean; 16 | 17 | resolve(value?: T | PromiseLike); 18 | 19 | reject(reason?: any); 20 | } 21 | 22 | class DeferredImpl implements Deferred { 23 | private _resolve!: (value?: T | PromiseLike) => void; 24 | private _reject!: (reason?: any) => void; 25 | private _resolved: boolean = false; 26 | private _rejected: boolean = false; 27 | private _promise: Promise; 28 | 29 | constructor(private _scope: any = null) { 30 | this._promise = new Promise((resolve, reject) => { 31 | this._resolve = resolve; 32 | this._reject = reject; 33 | }); 34 | } 35 | 36 | public get promise(): Promise { 37 | return this._promise; 38 | } 39 | 40 | public get resolved(): boolean { 41 | return this._resolved; 42 | } 43 | 44 | public get rejected(): boolean { 45 | return this._rejected; 46 | } 47 | 48 | public get completed(): boolean { 49 | return this._resolved || this._rejected; 50 | } 51 | 52 | public resolve(value?: T | PromiseLike) { 53 | this._resolve.apply(this._scope ? this._scope : this, arguments); 54 | this._resolved = true; 55 | } 56 | 57 | public reject(reason?: any) { 58 | this._reject.apply(this._scope ? this._scope : this, arguments); 59 | this._rejected = true; 60 | } 61 | } 62 | 63 | export function createDeferred(scope: any = null): Deferred { 64 | return new DeferredImpl(scope); 65 | } -------------------------------------------------------------------------------- /src/common/is.ts: -------------------------------------------------------------------------------- 1 | const toString = Object.prototype.toString; 2 | 3 | export function defined(value: any): boolean { 4 | return typeof value !== 'undefined'; 5 | } 6 | 7 | export function boolean(value: any): value is boolean { 8 | return value === true || value === false; 9 | } 10 | 11 | export function string(value: any): value is string { 12 | return toString.call(value) === '[object String]'; 13 | } -------------------------------------------------------------------------------- /src/common/lazy.ts: -------------------------------------------------------------------------------- 1 | export interface Lazy { 2 | readonly hasValue: boolean; 3 | readonly value: T; 4 | map(f: (x: T) => R): Lazy; 5 | } 6 | 7 | class LazyValue implements Lazy { 8 | private _hasValue: boolean = false; 9 | private _value?: T; 10 | 11 | constructor(private readonly _getValue: () => T) { } 12 | 13 | get hasValue(): boolean { 14 | return this._hasValue; 15 | } 16 | 17 | get value(): T { 18 | if (!this._hasValue) { 19 | this._hasValue = true; 20 | this._value = this._getValue(); 21 | } 22 | return this._value; 23 | } 24 | 25 | public map(f: (x: T) => R): Lazy { 26 | return new LazyValue(() => f(this.value)); 27 | } 28 | } 29 | 30 | export function lazy(getValue: () => T): Lazy { 31 | return new LazyValue(getValue); 32 | } -------------------------------------------------------------------------------- /src/common/logger.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { lazy } from './lazy'; 3 | import * as is from './is'; 4 | 5 | enum Trace { 6 | Off, 7 | Verbose 8 | } 9 | 10 | namespace Trace { 11 | export function fromString(value: string): Trace { 12 | value = value.toLowerCase(); 13 | switch (value) { 14 | case 'off': 15 | return Trace.Off; 16 | case 'verbose': 17 | return Trace.Verbose; 18 | default: 19 | return Trace.Off; 20 | } 21 | } 22 | } 23 | 24 | export class Logger { 25 | private _trace?: Trace; 26 | 27 | private readonly _outputChannel = lazy(() => vscode.window.createOutputChannel('PythonPreview')); 28 | 29 | constructor() { 30 | this.updateConfiguration(); 31 | } 32 | 33 | public updateConfiguration() { 34 | this._trace = this.readTrace(); 35 | } 36 | 37 | public info(message: string, data?: any): void { 38 | this.logLevel('Info', message, data); 39 | } 40 | 41 | public warn(message: string, data?: any): void { 42 | this.logLevel('Warn', message, data); 43 | } 44 | 45 | public error(message: string, data?: any): void { 46 | this.logLevel('Error', message, data); 47 | } 48 | 49 | public logLevel(level: string, message: string, data?: any): void { 50 | if (this._trace === Trace.Verbose) { 51 | this.appendLine(`[${level} - ${(new Date().toLocaleTimeString())}] ${message}`); 52 | if (data) { 53 | this.appendLine(Logger.data2String(data)); 54 | } 55 | } 56 | 57 | } 58 | 59 | private appendLine(value: string) { 60 | this._outputChannel.value.appendLine(value); 61 | } 62 | 63 | private static data2String(data: any): string { 64 | if (data instanceof Error) { 65 | if (is.string(data.stack)) { 66 | return data.stack; 67 | } 68 | return (data as Error).message; 69 | } 70 | if (is.boolean(data.success) && !data.success && is.string(data.message)) { 71 | return data.message; 72 | } 73 | if (is.string(data)) { 74 | return data; 75 | } 76 | return data.toString(); 77 | } 78 | 79 | private readTrace(): Trace { 80 | return Trace.fromString(vscode.workspace.getConfiguration().get('pythonPreview.trace', 'off')); 81 | } 82 | } -------------------------------------------------------------------------------- /src/common/net/socket/socketStream.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as net from "net"; 4 | 5 | const uint64be = require('uint64be'); 6 | 7 | export class SocketStream { 8 | private _socket: net.Socket; 9 | private _buffer: Buffer; 10 | private _isInTransaction: boolean; 11 | private _bytesRead: number = 0; 12 | private _hasInsufficientDataForReading: boolean = false; 13 | 14 | public constructor(socket: net.Socket, buffer: Buffer) { 15 | this._socket = socket; 16 | this._buffer = buffer; 17 | } 18 | 19 | public get buffer(): Buffer { 20 | return this._buffer; 21 | } 22 | 23 | public get hasInsufficientDataForReading(): boolean { 24 | return this._hasInsufficientDataForReading; 25 | } 26 | 27 | public get length(): number { 28 | return this._buffer.length; 29 | } 30 | 31 | public writeInt32(num: number) { 32 | this.writeInt64(num); 33 | } 34 | 35 | public writeInt64(num: number) { 36 | let buffer = uint64be.encode(num); 37 | this._socket.write(buffer); 38 | } 39 | 40 | public writeString(str: string) { 41 | let stringBuffer = Buffer.from(str); 42 | this.writeInt32(stringBuffer.length); 43 | if (stringBuffer.length > 0) { 44 | this._socket.write(stringBuffer); 45 | } 46 | } 47 | 48 | public write(buffer: Buffer) { 49 | this._socket.write(buffer); 50 | } 51 | 52 | public clearErrors() { 53 | this._hasInsufficientDataForReading = false; 54 | } 55 | 56 | public beginTransaction() { 57 | this._isInTransaction = true; 58 | this._bytesRead = 0; 59 | this.clearErrors(); 60 | } 61 | 62 | public endTransaction() { 63 | this._isInTransaction = false; 64 | this._buffer = this._buffer.slice(this._bytesRead); 65 | this._bytesRead = 0; 66 | this.clearErrors(); 67 | } 68 | 69 | public rollBackTransaction() { 70 | this._isInTransaction = false; 71 | this._bytesRead = 0; 72 | this.clearErrors(); 73 | } 74 | 75 | public toString(): string { 76 | return this._buffer.toString(); 77 | } 78 | 79 | public append(additionalData: Buffer) { 80 | if (this._buffer.length === 0) { 81 | this._buffer = additionalData; 82 | return; 83 | } 84 | let newBuffer = Buffer.alloc(this._buffer.length + additionalData.length); 85 | this._buffer.copy(newBuffer); 86 | additionalData.copy(newBuffer, this._buffer.length); 87 | this._buffer = newBuffer; 88 | } 89 | 90 | private isSufficientDataAvailable(length: number): boolean { 91 | if (this._buffer.length < (this._bytesRead + length)) { 92 | this._hasInsufficientDataForReading = true; 93 | } 94 | 95 | return !this._hasInsufficientDataForReading; 96 | } 97 | 98 | public readByte(): number { 99 | if (!this.isSufficientDataAvailable(1)) { 100 | return null; 101 | } 102 | 103 | let value = this._buffer.slice(this._bytesRead, this._bytesRead + 1)[0]; 104 | if (this._isInTransaction) { 105 | this._bytesRead++; 106 | } else { 107 | this._buffer = this._buffer.slice(1); 108 | } 109 | return value; 110 | } 111 | 112 | public readInt32(): number { 113 | return this.readInt64(); 114 | } 115 | 116 | public readInt64(): number { 117 | if (!this.isSufficientDataAvailable(8)) { 118 | return null; 119 | } 120 | 121 | let buf = this._buffer.slice(this._bytesRead, this._bytesRead + 8); 122 | 123 | if (this._isInTransaction) { 124 | this._bytesRead += 8; 125 | } else { 126 | this._buffer = this._buffer.slice(8); 127 | } 128 | 129 | return uint64be.decode(buf); 130 | } 131 | 132 | public readString(): string { 133 | let byteRead = this.readByte(); 134 | if (this._hasInsufficientDataForReading) { 135 | return null; 136 | } 137 | 138 | if (byteRead < 0) { 139 | throw new Error('IOException() - Socket.readString() failed to read string value'); 140 | } 141 | 142 | let type = Buffer.from([byteRead]).toString(); 143 | let isUnicode = false; 144 | switch (type) { 145 | case 'N': 146 | return null; 147 | case 'U': 148 | isUnicode = true; 149 | break; 150 | case 'A': 151 | isUnicode = false; 152 | break; 153 | default: 154 | throw new Error(`IOException() - Socket.readString() failed to parse unknown string type ${type}`); 155 | } 156 | 157 | let len = this.readInt32(); 158 | if (this._hasInsufficientDataForReading) { 159 | return null; 160 | } 161 | 162 | if (!this.isSufficientDataAvailable(len)) { 163 | return null; 164 | } 165 | 166 | let stringBuffer = this._buffer.slice(this._bytesRead, this._bytesRead + len); 167 | if (this._isInTransaction) { 168 | this._bytesRead += len; 169 | } else { 170 | this._buffer = this._buffer.slice(len); 171 | } 172 | 173 | return isUnicode ? stringBuffer.toString() : stringBuffer.toString('ascii'); 174 | } 175 | 176 | public readAsciiString(length: number): string { 177 | if (!this.isSufficientDataAvailable(length)) { 178 | return null; 179 | } 180 | 181 | let stringBuffer = this._buffer.slice(this._bytesRead, this._bytesRead + length); 182 | if (this._isInTransaction) { 183 | this._bytesRead += length; 184 | } else { 185 | this._buffer = this._buffer.slice(length); 186 | } 187 | 188 | return stringBuffer.toString('ascii'); 189 | } 190 | 191 | private readValueInTransaction(dataType: DataType, length?: number): T { 192 | let startedTransaction = false; 193 | if (!this._isInTransaction) { 194 | this.beginTransaction(); 195 | startedTransaction = true; 196 | } 197 | let data: any; 198 | switch (dataType) { 199 | case DataType.int32: 200 | data = this.readInt32(); 201 | break; 202 | case DataType.int64: 203 | data = this.readInt64(); 204 | break; 205 | case DataType.string: 206 | data = this.readString(); 207 | break; 208 | case DataType.asciiString: 209 | data = this.readAsciiString(length!); 210 | break; 211 | } 212 | 213 | if (this._hasInsufficientDataForReading) { 214 | if (startedTransaction) { 215 | this.rollBackTransaction(); 216 | } 217 | return undefined; 218 | } 219 | 220 | if (startedTransaction) { 221 | this.endTransaction(); 222 | } 223 | return data; 224 | } 225 | 226 | public readInt32InTransaction(): number { 227 | return this.readValueInTransaction(DataType.int32); 228 | } 229 | 230 | public readInt64InTransaction(): number { 231 | return this.readValueInTransaction(DataType.int64); 232 | } 233 | 234 | public readStringInTransaction(): string { 235 | return this.readValueInTransaction(DataType.string); 236 | } 237 | 238 | public readAsciiStringInTransaction(length: number): string { 239 | return this.readValueInTransaction(DataType.asciiString, length); 240 | } 241 | } 242 | 243 | enum DataType { 244 | int32, 245 | int64, 246 | string, 247 | asciiString 248 | } -------------------------------------------------------------------------------- /src/common/platform/pathUtils.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | 3 | export const IPathUtils = Symbol('IPathUtils'); 4 | 5 | export interface IPathUtils { 6 | getPathVariableName(): 'Path' | 'PATH'; 7 | basename(pathValue: string, ext?: string): string; 8 | } 9 | 10 | export class PathUtils implements IPathUtils { 11 | constructor(private _isWindows: boolean) { } 12 | 13 | public getPathVariableName() { 14 | return this._isWindows ? 'Path' : 'PATH'; 15 | } 16 | 17 | public basename(pathValue: string, ext?: string): string { 18 | return path.basename(pathValue, ext); 19 | } 20 | } -------------------------------------------------------------------------------- /src/debugger/common/contracts.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as net from "net"; 4 | 5 | export interface IPythonProcess extends NodeJS.EventEmitter { 6 | connect(buffer: Buffer, socket: net.Socket): boolean; 7 | handleInComingData(buffer: Buffer); 8 | detach(); 9 | kill(); 10 | } 11 | 12 | export interface IDebugServer { 13 | port: number; 14 | host?: string; 15 | } 16 | 17 | export interface LaunchRequestArguments { 18 | pythonPath: string; 19 | port?: number; 20 | host?: string; 21 | } -------------------------------------------------------------------------------- /src/debugger/common/utils.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import untildify = require("untildify"); 4 | import * as path from "path"; 5 | import * as child_process from "child_process"; 6 | export const IS_WINDOWS = /^win/.test(process.platform); 7 | 8 | /** 9 | * 获取python可执行文件. 10 | * 11 | * @param {string} pythonPath 12 | * @returns {string} 13 | */ 14 | export function getPythonExecutable(pythonPath: string): string { 15 | pythonPath = untildify(pythonPath); 16 | 17 | if (pythonPath === 'python' || 18 | pythonPath.indexOf(path.sep) === -1 || 19 | path.basename(pythonPath) === path.dirname(pythonPath)) { 20 | return pythonPath; 21 | } 22 | 23 | if (isValidPythonPath(pythonPath)) { 24 | return pythonPath; 25 | } 26 | 27 | const knownPythonExecutables = ['python', 'python3.7', 'python3.6', 'python3.5', 'python3', 'python2.7', 'python2']; 28 | 29 | for (let executableName of knownPythonExecutables) { 30 | // Suffix with 'python' for linux and 'osx', and 'python.exe' for 'windows'. 31 | if (IS_WINDOWS) { 32 | executableName = `${executableName}.exe`; 33 | if (isValidPythonPath(path.join(pythonPath, executableName))) { 34 | return path.join(pythonPath, executableName); 35 | } 36 | if (isValidPythonPath(path.join(pythonPath, 'scripts', executableName))) { 37 | return path.join(pythonPath, 'scripts', executableName); 38 | } 39 | } else { 40 | if (isValidPythonPath(path.join(pythonPath, executableName))) { 41 | return path.join(pythonPath, executableName); 42 | } 43 | if (isValidPythonPath(path.join(pythonPath, 'bin', executableName))) { 44 | return path.join(pythonPath, executableName); 45 | } 46 | } 47 | } 48 | 49 | return pythonPath; 50 | } 51 | 52 | /** 53 | * python可执行文件路径是否合法. 54 | * 55 | * @param {string} pythonPath 56 | * @returns {boolean} 57 | */ 58 | function isValidPythonPath(pythonPath: string): boolean { 59 | try { 60 | const output = child_process.execFileSync(pythonPath, ['-c', 'print(1234)'], {encoding: 'utf8'}); 61 | (output as string).startsWith('1234'); 62 | } catch (e) { 63 | return false; 64 | } 65 | } -------------------------------------------------------------------------------- /src/debugger/debugClients/baseDebugClient.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {EventEmitter} from "events"; 4 | import {IDebugServer, IPythonProcess} from "../common/contracts"; 5 | import {BaseDebugServer} from "../debugServers/baseDebugServer"; 6 | 7 | export abstract class BaseDebugClient extends EventEmitter { 8 | constructor(protected _args: T) { 9 | super(); 10 | } 11 | 12 | public abstract createDebugServer(pythonProcess?: IPythonProcess): BaseDebugServer; 13 | 14 | public stop() { } 15 | 16 | public launchApplicationToDebug(debugServer: IDebugServer): Promise { 17 | return Promise.resolve(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/debugger/debugClients/launcherProvider.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as path from "path"; 4 | 5 | export interface IDebugLauncherScriptProvider { 6 | getLauncherFilePath(): string; 7 | } 8 | 9 | export class DebuggerLauncherScriptProvider implements IDebugLauncherScriptProvider { 10 | public getLauncherFilePath(): string { 11 | return path.join(path.dirname(__dirname), '..', '..', 'pythonFiles', 'pydev', 'launcher.py'); 12 | } 13 | } -------------------------------------------------------------------------------- /src/debugger/debugClients/localDebugClient.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from "vscode"; 4 | import {BaseDebugClient} from "./baseDebugClient"; 5 | import {IDebugServer, IPythonProcess, LaunchRequestArguments} from "../common/contracts"; 6 | import {ChildProcess, spawn} from "child_process"; 7 | import {BaseDebugServer} from "../debugServers/baseDebugServer"; 8 | import {IDebugLauncherScriptProvider} from "./launcherProvider"; 9 | import {LocalDebugServer} from "../debugServers/localDebugServer"; 10 | import { isNotInstalledError } from "../../common/helpers"; 11 | import { Logger } from "../../common/logger"; 12 | import { PythonPreviewManager } from "../../features/previewManager"; 13 | 14 | enum DebugServerStatus { 15 | Unknown = 1, 16 | Running = 2, 17 | NotRunning = 3 18 | } 19 | 20 | export class LocalDebugClient extends BaseDebugClient { 21 | protected _pyProc: ChildProcess | undefined; 22 | protected _pythonProcess!: IPythonProcess; 23 | protected _debugServer: BaseDebugServer | undefined; 24 | 25 | constructor(args: LaunchRequestArguments, private _launcherScriptProvider: IDebugLauncherScriptProvider, private _previewManager: PythonPreviewManager, private _logger: Logger) { 26 | super(args); 27 | } 28 | 29 | private get debugServerStatus(): DebugServerStatus { 30 | if (this._debugServer) { 31 | switch (this._debugServer!.isRunning) { 32 | case true: 33 | return DebugServerStatus.Running; 34 | case false: 35 | return DebugServerStatus.NotRunning; 36 | } 37 | } 38 | return DebugServerStatus.Unknown; 39 | } 40 | 41 | public createDebugServer(pythonProcess?: IPythonProcess): BaseDebugServer { 42 | this._pythonProcess = pythonProcess!; 43 | this._debugServer = new LocalDebugServer(this._pythonProcess!, this._args, this._logger); 44 | return this._debugServer; 45 | } 46 | 47 | public stop() { 48 | if (this._debugServer) { 49 | this._debugServer!.stop(); 50 | this._debugServer = undefined; 51 | } 52 | if (this._pyProc) { 53 | this._pyProc.kill(); 54 | this._pyProc = undefined; 55 | } 56 | } 57 | 58 | private displayError(error: any) { 59 | let errorMsg = typeof error === 'string' ? error : ((error.message && error.message.length > 0) ? error.message : ''); 60 | if (isNotInstalledError(error)) { 61 | errorMsg = `Failed to launch the Python Process, please validate the path '${this._args.pythonPath}'`; 62 | } 63 | if (errorMsg.length > 0) { 64 | vscode.window.showErrorMessage(errorMsg); 65 | this._logger.error(errorMsg); 66 | } 67 | } 68 | 69 | public async launchApplicationToDebug(debugServer: IDebugServer): Promise { 70 | return new Promise((resolve, reject) => { 71 | let pythonPath = 'python'; 72 | if (typeof this._args.pythonPath === 'string' && this._args.pythonPath.trim().length > 0) { 73 | pythonPath = this._args.pythonPath; 74 | } 75 | const args = this.buildLaunchArguments(debugServer.port); 76 | this._logger.info('Starting Debug Client'); 77 | this._pyProc = spawn(pythonPath, args); 78 | this.handleProcessOutput(this._pyProc!, reject); 79 | 80 | // Here we wait for the application to connect to the socket server. 81 | // Only once connected do we know that the application has successfully launched. 82 | this._debugServer!.debugClientConnected 83 | .then(resolve) 84 | .catch(ex => console.error('Python Client Connect Exception: _debugServer.debugClientConnected', ex)); 85 | }) 86 | } 87 | 88 | protected handleProcessOutput(proc: ChildProcess, failedToLaunch: (error: Error | string | Buffer) => void) { 89 | proc.on('error', error => { 90 | const status = this.debugServerStatus; 91 | if (status === DebugServerStatus.Running) { 92 | return; 93 | } 94 | if (status === DebugServerStatus.NotRunning && typeof(error) === 'object' && error !== null) { 95 | return failedToLaunch(error); 96 | } 97 | // This could happen when the debugger didn't launch at all, e.g. python doesn't exist. 98 | this.displayError(error); 99 | this._previewManager.dispose(); 100 | }); 101 | 102 | proc.stderr.setEncoding('utf8'); 103 | proc.stderr.on('data', error => { 104 | // if (this.debugServerStatus === DebugServerStatus.NotRunning) { 105 | // return failedToLaunch(error); 106 | // } 107 | let x = 0; 108 | }); 109 | 110 | proc.stdout.setEncoding('utf-8'); 111 | proc.stdout.on('data', d => { 112 | const arr = d.toString().split('&'); 113 | const length = arr.length; 114 | if (length <= 2) return; 115 | const dataType = arr[length - 2]; 116 | const dataMessage = arr[length - 1]; 117 | if (dataType === 'info') { 118 | this._logger.info(dataMessage); 119 | } else if (dataType === 'warn') { 120 | this._logger.warn(dataMessage); 121 | } else if (dataType === 'error') { 122 | this.displayError(dataMessage); 123 | this._previewManager.dispose(); 124 | } 125 | }); 126 | } 127 | 128 | private buildLaunchArguments(debugPort: number): string[] { 129 | return [...this.buildDebugArguments(debugPort)]; 130 | } 131 | 132 | private buildDebugArguments(debugPort: number): string[] { 133 | const launcherFilePath = this._launcherScriptProvider.getLauncherFilePath(); 134 | return [launcherFilePath, debugPort.toString(), 'ba648ec3-d025-44bb-92fb-7ded4267a243']; 135 | } 136 | } -------------------------------------------------------------------------------- /src/debugger/debugServers/baseDebugServer.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {EventEmitter} from "events"; 4 | import {createDeferred, Deferred} from "../../common/helpers"; 5 | import {Socket} from "net"; 6 | import {IDebugServer, IPythonProcess} from "../common/contracts"; 7 | 8 | export abstract class BaseDebugServer extends EventEmitter { 9 | protected _clientSocket: Deferred; 10 | protected _pythonProcess:IPythonProcess; 11 | protected _isRunning: boolean; 12 | protected _debugClientConnected: Deferred; 13 | 14 | constructor(pythonProcess?: IPythonProcess) { 15 | super(); 16 | this._pythonProcess = pythonProcess; 17 | this._debugClientConnected = createDeferred(); 18 | this._clientSocket = createDeferred(); 19 | } 20 | 21 | public get isRunning(): boolean { 22 | return this._isRunning; 23 | } 24 | 25 | public get debugClientConnected(): Promise { 26 | return this._debugClientConnected.promise; 27 | } 28 | 29 | public abstract start(): Promise; 30 | 31 | public abstract stop(); 32 | } -------------------------------------------------------------------------------- /src/debugger/debugServers/localDebugServer.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from 'vscode'; 4 | import {BaseDebugServer} from "./baseDebugServer"; 5 | import * as net from "net"; 6 | import {IDebugServer, IPythonProcess, LaunchRequestArguments} from "../common/contracts"; 7 | import { Logger } from '../../common/logger'; 8 | 9 | export class LocalDebugServer extends BaseDebugServer { 10 | private _debugSocketServer: net.Server | undefined; 11 | 12 | constructor(pythonProcess: IPythonProcess | undefined, private _args: LaunchRequestArguments, private _logger: Logger) { 13 | super(pythonProcess); 14 | } 15 | 16 | public stop() { 17 | if (!this._debugSocketServer) { 18 | return; 19 | } 20 | 21 | try { 22 | this._debugSocketServer.close(); 23 | } catch { } 24 | 25 | this._debugSocketServer = undefined; 26 | } 27 | 28 | public async start(): Promise { 29 | return new Promise((resolve, reject) => { 30 | let connectedResolve = this._debugClientConnected.resolve.bind(this._debugClientConnected); 31 | let connected = false; 32 | let disconnected = false; 33 | this._debugSocketServer = net.createServer(c => { 34 | // "connection"监听器 35 | c.on('data', (buffer: Buffer) => { 36 | if (connectedResolve) { 37 | // debug客户端已经连接到debug服务器 38 | connectedResolve(true); 39 | this._logger.info('Debug Client Connected'); 40 | connectedResolve = null; 41 | } 42 | if (!connected) { 43 | connected = this._pythonProcess.connect(buffer, c); 44 | } else { 45 | this._pythonProcess.handleInComingData(buffer); 46 | this._isRunning = true; 47 | } 48 | }); 49 | c.on('close', d => { 50 | disconnected = true; 51 | this.emit('detach', d); 52 | }); 53 | c.on('timeout', () => { 54 | const msg = `Debugger client timeout.`; 55 | this._logger.warn(msg); 56 | }); 57 | c.on('error', ex => { 58 | if (connected || disconnected) { 59 | return; 60 | } 61 | const msg = `There was an error in starting the debug server. Error = ${JSON.stringify(ex)}`; 62 | reject(msg); 63 | }); 64 | }); 65 | 66 | this._debugSocketServer!.on('error', ex => { 67 | const exMsg = JSON.stringify(ex); 68 | let msg = ''; 69 | if ((ex as any).code === 'EADDRINUSE') { 70 | msg = `The port used for debugging is in use, please try again or try restarting Visual Studio Code, Error = ${exMsg}`; 71 | } else { 72 | if (connected) { 73 | return; 74 | } 75 | msg = `There was an error in starting the debug server. Error = ${exMsg}`; 76 | } 77 | reject(msg); 78 | }); 79 | 80 | const port = typeof this._args.port === 'number' ? this._args.port! : 0; 81 | const host = typeof this._args.host === 'string' && this._args.host!.trim().length > 0 ? this._args.host!.trim() : 'localhost'; 82 | this._debugSocketServer!.listen({port: port, host: host}, () => { 83 | const server = this._debugSocketServer!.address(); 84 | resolve({port: server.port}); 85 | }) 86 | }); 87 | } 88 | } -------------------------------------------------------------------------------- /src/debugger/proxyCommands.ts: -------------------------------------------------------------------------------- 1 | export class Commands { 2 | public static OutputCommandBytes: Buffer = Buffer.from('outp'); 3 | public static DetachCommandBytes: Buffer = Buffer.from('detc'); 4 | } -------------------------------------------------------------------------------- /src/debugger/pythonProcess.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {EventEmitter} from "events"; 4 | import {IPythonProcess} from "./common/contracts"; 5 | import {PythonProcessCallbackHandler} from "./pythonProcessCallbackHandler"; 6 | import {SocketStream} from "../common/net/socket/socketStream"; 7 | import * as net from "net"; 8 | import { Commands } from "./proxyCommands"; 9 | 10 | export class PythonProcess extends EventEmitter implements IPythonProcess { 11 | private _id: number; 12 | private _guid: string; 13 | private _callbackHandler: PythonProcessCallbackHandler; 14 | private _stream: SocketStream; 15 | private _guidRead: boolean; 16 | private _statusRead: boolean; 17 | private _pidRead: boolean; 18 | private _pid: number; 19 | 20 | constructor(id: number, guid: string) { 21 | super(); 22 | this._id = id; 23 | this._guid = guid; 24 | } 25 | 26 | public kill() { 27 | if (this._pid && typeof this._pid === 'number') { 28 | try { 29 | let kill = require('tree-kill'); 30 | kill(this._pid!); 31 | this._pid = undefined; 32 | } catch (e) { 33 | } 34 | 35 | } 36 | } 37 | 38 | public detach() { 39 | this._stream.write(Buffer.from('detc')); 40 | } 41 | 42 | public connect(buffer: Buffer, socket: net.Socket): boolean { 43 | if (!this._stream) { 44 | this._stream = new SocketStream(socket, buffer); 45 | } else { 46 | this._stream.append(buffer); 47 | } 48 | 49 | if (!this._guidRead) { 50 | this._stream.beginTransaction(); 51 | this._stream.readString(); 52 | if (this._stream.hasInsufficientDataForReading) { 53 | this._stream.rollBackTransaction(); 54 | return false; 55 | } 56 | this._guidRead = true; 57 | this._stream.endTransaction(); 58 | } 59 | 60 | if (!this._statusRead) { 61 | this._stream.beginTransaction(); 62 | this._stream.readInt32(); 63 | if (this._stream.hasInsufficientDataForReading) { 64 | this._stream.rollBackTransaction(); 65 | return false; 66 | } 67 | this._statusRead = true; 68 | this._stream.endTransaction(); 69 | } 70 | 71 | if (!this._pidRead) { 72 | this._stream.beginTransaction(); 73 | this._pid = this._stream.readInt32(); 74 | if (this._stream.hasInsufficientDataForReading) { 75 | this._stream.rollBackTransaction(); 76 | return false; 77 | } 78 | this._pidRead = true; 79 | this._stream.endTransaction(); 80 | } 81 | 82 | this._callbackHandler = new PythonProcessCallbackHandler(this, this._stream); 83 | this._callbackHandler.on('processLoaded', pythonVersion => this.emit('processLoaded', pythonVersion)); 84 | this._callbackHandler.on('output', (fileName, output) => this.emit('output', fileName, output)); 85 | this._callbackHandler.on('detach', () => this.emit('detach')); 86 | this._callbackHandler.handleIncomingData(); 87 | return true; 88 | } 89 | 90 | public handleInComingData(buffer: Buffer) { 91 | this._stream.append(buffer); 92 | 93 | if (!this._guidRead) { 94 | this._stream.rollBackTransaction(); 95 | this._stream.readString(); 96 | if (this._stream.hasInsufficientDataForReading) { 97 | return; 98 | } 99 | this._guidRead = true; 100 | this._stream.endTransaction(); 101 | } 102 | 103 | if (!this._statusRead) { 104 | this._stream.beginTransaction(); 105 | this._stream.readInt32(); 106 | if (this._stream.hasInsufficientDataForReading) { 107 | this._stream.rollBackTransaction(); 108 | return; 109 | } 110 | this._pidRead = true; 111 | this._stream.endTransaction(); 112 | } 113 | 114 | this._callbackHandler.handleIncomingData(); 115 | } 116 | 117 | public sendExecutableText(folder: string, fileName: string, code: string, cumulativeModde: boolean, allowAllModules: boolean, maxExecutedLines: number) { 118 | this._stream.write(Commands.OutputCommandBytes); 119 | if (cumulativeModde) { 120 | this._stream.writeInt64(1); 121 | } else { 122 | this._stream.writeInt64(0); 123 | } 124 | if (allowAllModules) { 125 | this._stream.writeInt64(1); 126 | } else { 127 | this._stream.writeInt64(0); 128 | } 129 | this._stream.writeInt64(maxExecutedLines); 130 | this._stream.writeString(folder); 131 | this._stream.writeString(fileName); 132 | this._stream.writeString(code); 133 | } 134 | } -------------------------------------------------------------------------------- /src/debugger/pythonProcessCallbackHandler.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {EventEmitter} from "events"; 4 | import {IPythonProcess} from "./common/contracts"; 5 | import {SocketStream} from "../common/net/socket/socketStream"; 6 | 7 | export class PythonProcessCallbackHandler extends EventEmitter { 8 | private _pythonProcess: IPythonProcess; 9 | private _stream: SocketStream; 10 | 11 | constructor(pythonProcess: IPythonProcess, stream: SocketStream) { 12 | super(); 13 | this._pythonProcess = pythonProcess; 14 | this._stream =stream; 15 | } 16 | 17 | public handleIncomingData() { 18 | if (this._stream.length === 0) { 19 | return; 20 | } 21 | 22 | this._stream.beginTransaction(); 23 | 24 | let cmd = this._stream.readAsciiString(4); 25 | if (this._stream.hasInsufficientDataForReading) { 26 | return; 27 | } 28 | 29 | switch (cmd) { 30 | case 'LOAD': this.handleProcessLoad(); break; 31 | case 'OUTP': this.handleDebuggerOutput(); break; 32 | case 'DETC': this.handleDetach(); break; 33 | default: 34 | this.emit('error', `Unhandled command '${cmd}'`); 35 | } 36 | 37 | if (this._stream.hasInsufficientDataForReading) { 38 | this._stream.rollBackTransaction(); 39 | return; 40 | } 41 | 42 | this._stream.endTransaction(); 43 | if (this._stream.length > 0) { 44 | this.handleIncomingData(); 45 | } 46 | } 47 | 48 | private handleProcessLoad() { 49 | let pythonVersion = this._stream.readString(); 50 | if (this._stream.hasInsufficientDataForReading) { 51 | return; 52 | } 53 | this.emit('processLoaded', pythonVersion); 54 | } 55 | 56 | private handleDebuggerOutput() { 57 | let fileName = this._stream.readString(); 58 | let output = this._stream.readString(); 59 | if (this._stream.hasInsufficientDataForReading) { 60 | return; 61 | } 62 | this.emit("output", fileName, output); 63 | } 64 | 65 | private handleDetach() { 66 | this.emit('detach'); 67 | } 68 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { Logger } from './common/logger'; 3 | import { PythonContentProvider } from './features/previewContentProvider'; 4 | import { PythonPreviewManager } from './features/previewManager'; 5 | import { CommandManager } from './common/commandManager'; 6 | import * as commands from './commands'; 7 | 8 | export function activate(context: vscode.ExtensionContext) { 9 | const logger = new Logger(); 10 | 11 | const contentProvider = new PythonContentProvider(context, logger); 12 | const previewManager = new PythonPreviewManager(context, contentProvider, logger); 13 | context.subscriptions.push(previewManager); 14 | 15 | const commandManager = new CommandManager(); 16 | context.subscriptions.push(commandManager); 17 | commandManager.register(new commands.ShowPreviewCommand(previewManager)); 18 | commandManager.register(new commands.ShowPreviewToSideCommand(previewManager)); 19 | commandManager.register(new commands.ShowLockedPreviewToSideCommand(previewManager)); 20 | commandManager.register(new commands.ShowSourceCommand(previewManager)); 21 | commandManager.register(new commands.RefreshPreviewCommand(previewManager)); 22 | commandManager.register(new commands.ToggleLockCommand(previewManager)); 23 | 24 | context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { 25 | logger.updateConfiguration(); 26 | previewManager.updateConfiguration(); 27 | })) 28 | } -------------------------------------------------------------------------------- /src/features/preview.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from "path"; 3 | 4 | import {PythonContentProvider} from "./previewContentProvider"; 5 | import {PythonPreviewConfigurationManager} from "./previewConfig"; 6 | import {Logger} from "../common/logger"; 7 | import {disposeAll} from "../common/dispose"; 8 | import { PythonPreviewManager } from './previewManager'; 9 | import { isPythonFile } from '../common/file'; 10 | import { PythonOutput, PythonOutputStatus } from './pythonOutput'; 11 | 12 | export interface PreviewState { 13 | readonly resource: string; 14 | readonly locked: boolean; 15 | readonly startingInstruction: number 16 | readonly width: number; 17 | } 18 | 19 | export class PythonPreview { 20 | public static viewtype = 'pythonPreview'; 21 | 22 | private _resource: vscode.Uri; 23 | private _locked: boolean; 24 | private _startingInstrcution: number | undefined; 25 | 26 | private _codAndNavWidth: number | undefined; 27 | 28 | private readonly _webviewPanel: vscode.WebviewPanel; 29 | private readonly _disposables: vscode.Disposable[] = []; 30 | private _currentVersion?: { resource: vscode.Uri, version: number }; 31 | private _disposed: boolean = false; 32 | private readonly _onDidDisposeEmitter = new vscode.EventEmitter(); 33 | public readonly onDidDispose = this._onDidDisposeEmitter.event; 34 | 35 | private readonly _onDidChangeViewStateEmitter = new vscode.EventEmitter(); 36 | public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; 37 | 38 | 39 | 40 | // 反序列化时使用 41 | public static async receive(webviewPanel: vscode.WebviewPanel, 42 | state: PreviewState, 43 | previewManager: PythonPreviewManager, 44 | context: vscode.ExtensionContext, 45 | cachedOutputs: Map, 46 | contentProvider: PythonContentProvider, 47 | previewConfigurationManager: PythonPreviewConfigurationManager, 48 | logger: Logger): Promise { 49 | const resource = vscode.Uri.parse(state.resource); 50 | const locked = state.locked; 51 | const startingInstruction = state.startingInstruction; 52 | const width = state.width; 53 | 54 | const preview = new PythonPreview(webviewPanel, 55 | resource, 56 | locked, 57 | startingInstruction, 58 | width, 59 | previewManager, 60 | context, 61 | cachedOutputs, 62 | contentProvider, 63 | previewConfigurationManager, 64 | logger); 65 | 66 | await preview.doUpdate(); 67 | return preview; 68 | } 69 | 70 | public static create(resource: vscode.Uri, 71 | previewColumn: vscode.ViewColumn, 72 | locked: boolean, 73 | previewManager: PythonPreviewManager, 74 | context: vscode.ExtensionContext, 75 | cachedOutputs: Map, 76 | contentProvider: PythonContentProvider, 77 | previewConfigurationManager: PythonPreviewConfigurationManager, 78 | logger: Logger): PythonPreview { 79 | const webviewPanel = vscode.window.createWebviewPanel(PythonPreview.viewtype, 80 | PythonPreview.getPreviewTitle(resource, locked), 81 | previewColumn, 82 | { 83 | enableFindWidget: true, 84 | ...PythonPreview.getWebviewOptions(resource, context) 85 | }); 86 | return new PythonPreview(webviewPanel, 87 | resource, 88 | locked, 89 | undefined, 90 | undefined, 91 | previewManager, 92 | context, 93 | cachedOutputs, 94 | contentProvider, 95 | previewConfigurationManager, 96 | logger); 97 | } 98 | 99 | private constructor(webviewPanel: vscode.WebviewPanel, 100 | resource: vscode.Uri, 101 | locked: boolean, 102 | staringInstruction: number | undefined, 103 | width: number | undefined, 104 | private readonly _previewManager: PythonPreviewManager, 105 | private readonly _context: vscode.ExtensionContext, 106 | private readonly _cachedOutputs: Map, 107 | private readonly _contentProvider: PythonContentProvider, 108 | private readonly _previewConfigurationManager: PythonPreviewConfigurationManager, 109 | private readonly _logger: Logger) { 110 | this._resource = resource; 111 | this._locked = locked; 112 | this._startingInstrcution = staringInstruction; 113 | this._codAndNavWidth = width; 114 | this._webviewPanel = webviewPanel; 115 | 116 | this._webviewPanel.onDidDispose(() => { 117 | this.dispose(); 118 | }, null, this._disposables); 119 | 120 | this._webviewPanel.onDidChangeViewState(e => { 121 | this.updateContentWithStatus(true); 122 | this._onDidChangeViewStateEmitter.fire(e); 123 | }, null, this._disposables); 124 | 125 | // 处理来自webview的消息 126 | this._webviewPanel.webview.onDidReceiveMessage(e => { 127 | if (e.source !== this._resource.toString()) { 128 | return; 129 | } 130 | 131 | switch (e.type) { 132 | case 'command': 133 | vscode.commands.executeCommand(e.body.command, ...e.body.args); 134 | break; 135 | case 'updateStartingInstruction': 136 | this.onDidUpdateStartingInstruction(e.body.curInstr); 137 | break; 138 | case 'updateCodAndNavWidth': 139 | this.onDidUpdataCodAndNavWidth(e.body.width); 140 | break; 141 | } 142 | }, null, this._disposables); 143 | 144 | vscode.workspace.onDidChangeTextDocument(event => { 145 | if (this.isPreviewOf(event.document.uri)) { 146 | // 文本改变直接传送给调试器,等待调试器返回trace 147 | this._previewManager.postMessageToDebugger(event.document.fileName, event.document.getText()); 148 | } 149 | }, null, this._disposables); 150 | 151 | vscode.window.onDidChangeActiveTextEditor(editor => { 152 | if (editor && isPythonFile(editor.document) && !this._locked) { 153 | this.update(editor.document.uri); 154 | } 155 | }, null, this._disposables); 156 | } 157 | 158 | public get resource(): vscode.Uri { 159 | return this._resource; 160 | } 161 | 162 | public get locked(): boolean { 163 | return this._locked; 164 | } 165 | 166 | public get state(): PreviewState { 167 | return { 168 | resource: this._resource.toString(), 169 | locked: this._locked, 170 | startingInstruction: this._startingInstrcution, 171 | width: this._codAndNavWidth 172 | }; 173 | } 174 | 175 | public get visibale(): boolean { 176 | return this._webviewPanel.visible; 177 | } 178 | 179 | public get position(): vscode.ViewColumn | undefined { 180 | return this._webviewPanel.viewColumn; 181 | } 182 | 183 | public get disposed(): boolean { 184 | return this._disposed; 185 | } 186 | 187 | public dispose() { 188 | if (this._disposed) { 189 | return; 190 | } 191 | 192 | this._disposed = true; 193 | this._onDidDisposeEmitter.fire(); 194 | 195 | this._onDidDisposeEmitter.dispose(); 196 | this._onDidChangeViewStateEmitter.dispose(); 197 | this._webviewPanel.dispose(); 198 | 199 | disposeAll(this._disposables); 200 | 201 | } 202 | 203 | public updateConfiguration() { 204 | if (this._previewConfigurationManager.hasConfigurationChanged(this._resource)) { 205 | this.initialContent(); 206 | } 207 | } 208 | 209 | public update(resource: vscode.Uri) { 210 | const isResourceChange = resource.fsPath !== this._resource.fsPath; 211 | if (isResourceChange) { 212 | this._resource = resource; 213 | this._startingInstrcution = undefined; 214 | this.initialContent(); 215 | } 216 | } 217 | 218 | public async doUpdate(): Promise { 219 | const document = await vscode.workspace.openTextDocument(this._resource); 220 | this._currentVersion = { resource: this._resource, version: document.version }; 221 | this._webviewPanel.title = PythonPreview.getPreviewTitle(this._resource, this._locked); 222 | this._webviewPanel.webview.options = PythonPreview.getWebviewOptions(this._resource, this._context); 223 | this._webviewPanel.webview.html = this._contentProvider.provideTextDocumentContent(document, this._previewConfigurationManager, this.state); 224 | this._previewManager.postMessageToDebugger(document.fileName, document.getText()); 225 | } 226 | 227 | public async initialContent(): Promise { 228 | const document = await vscode.workspace.openTextDocument(this._resource); 229 | 230 | this._webviewPanel.title = PythonPreview.getPreviewTitle(this._resource, this._locked); 231 | this._webviewPanel.webview.options = PythonPreview.getWebviewOptions(this._resource, this._context); 232 | this._webviewPanel.webview.html = this._contentProvider.provideTextDocumentContent(document, this._previewConfigurationManager, this.state); 233 | 234 | this._previewManager.postMessageToDebugger(document.fileName, document.getText()); 235 | } 236 | 237 | public async updateContent(): Promise { 238 | const document = await vscode.workspace.openTextDocument(this._resource); 239 | if (this._currentVersion && this._resource.fsPath === this._currentVersion.resource.fsPath && document.version === this._currentVersion.version) { 240 | this.updateContentWithStatus(true); 241 | } else { 242 | this.updateContentWithStatus(false); 243 | } 244 | this._currentVersion = { resource: this._resource, version: document.version }; 245 | } 246 | 247 | public updateStatus() { 248 | this._startingInstrcution = undefined; 249 | } 250 | 251 | public updateContentWithStatus(hasStatus: boolean) { 252 | const cacheOutput = this._cachedOutputs.get(this._resource.fsPath); 253 | // 如果此时还没有缓存的输出或者正在调试中,则直接返回 254 | if (!cacheOutput || cacheOutput.status !== PythonOutputStatus.Prcoessed) return; 255 | const config = this._previewConfigurationManager.getConfigCacheForResource(this._resource); 256 | if (this._codAndNavWidth === undefined) { 257 | this._codAndNavWidth = config.contentConfig.codAndNavWidth; 258 | } 259 | const options = { 260 | jumpToEnd: true, 261 | startingInstruction: undefined, 262 | disableHeapNesting: config.contentConfig.disableHeapNesting, 263 | textualMemoryLabels: config.contentConfig.textualMemoryLabels, 264 | compactFuncLabels: config.contentConfig.compactFuncLabels, 265 | showAllFrameLabels: config.contentConfig.showAllFrameLabels, 266 | hideCode: config.contentConfig.hideCode, 267 | lang: this._previewManager.lang, 268 | width: this._codAndNavWidth 269 | }; 270 | if (hasStatus) options.startingInstruction = this._startingInstrcution; 271 | if (this.position) { 272 | this._logger.info(`Updating ${PythonPreview.getPreviewTitle(this._resource, this._locked)} (Group ${this.position})`); 273 | } else { 274 | this._logger.info(`Updating ${PythonPreview.getPreviewTitle(this._resource, this._locked)}`); 275 | } 276 | this.postMessage({ 277 | type: 'updateContent', 278 | data: cacheOutput.trace, 279 | options: options 280 | }); 281 | } 282 | 283 | public matchesResource(otherResource: vscode.Uri, 284 | otherPosition: vscode.ViewColumn | undefined, 285 | otherLocked: boolean): boolean { 286 | if (this.position !== otherPosition) { 287 | return false; 288 | } 289 | 290 | if (this._locked !== otherLocked) { 291 | return false; 292 | } 293 | 294 | return this.isPreviewOf(otherResource); 295 | } 296 | 297 | public matches(otherPreview: PythonPreview): boolean { 298 | return this.matchesResource(otherPreview._resource, otherPreview.position, otherPreview._locked); 299 | } 300 | 301 | public reveal(viewColumn: vscode.ViewColumn) { 302 | this._webviewPanel.reveal(viewColumn); 303 | } 304 | 305 | public toggleLock() { 306 | this._locked = !this._locked; 307 | this._webviewPanel.title = PythonPreview.getPreviewTitle(this._resource, this._locked); 308 | this.postMessage({ 309 | type: 'updateLock', 310 | locked: this._locked 311 | }); 312 | } 313 | 314 | public isPreviewOf(resource: vscode.Uri): boolean { 315 | return this._resource.fsPath === resource.fsPath; 316 | } 317 | 318 | public static getPreviewTitle(resource: vscode.Uri, locked: boolean): string { 319 | return locked 320 | ? `[Preview] ${path.basename(resource.fsPath)}` 321 | : `Preview ${path.basename(resource.fsPath)}`; 322 | } 323 | 324 | private postMessage(msg: any) { 325 | if (!this._disposed) { 326 | this._webviewPanel.webview.postMessage(msg); 327 | } 328 | } 329 | 330 | private static getWebviewOptions(resource: vscode.Uri, context: vscode.ExtensionContext): vscode.WebviewOptions { 331 | return { 332 | enableScripts: true, 333 | enableCommandUris: true, 334 | localResourceRoots: PythonPreview.getLocalResourceRoots(resource, context) 335 | }; 336 | } 337 | 338 | private static getLocalResourceRoots(resource: vscode.Uri, context: vscode.ExtensionContext): vscode.Uri[] { 339 | const baseRoots = [vscode.Uri.file(context.extensionPath)]; 340 | 341 | const folder = vscode.workspace.getWorkspaceFolder(resource); 342 | folder && baseRoots.push(folder.uri); 343 | 344 | (!resource.scheme || resource.scheme === 'file') && baseRoots.push(vscode.Uri.file(path.dirname(resource.fsPath))); 345 | 346 | return baseRoots; 347 | } 348 | 349 | private onDidUpdateStartingInstruction(curInstr: number) { 350 | this._startingInstrcution = curInstr; 351 | } 352 | 353 | private onDidUpdataCodAndNavWidth(width: number) { 354 | this._codAndNavWidth = width; 355 | } 356 | } -------------------------------------------------------------------------------- /src/features/previewConfig.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | 4 | enum ReloadType { 5 | ReloadStyle = 1, 6 | 7 | ReloadContent = 2, 8 | 9 | ReloadTrace = 3 10 | } 11 | 12 | class StyleConfiguration { 13 | [key: string]: any; 14 | public readonly fontFamily: string | undefined; 15 | public readonly fontSize: number; 16 | public readonly langDisplayFF: string | undefined; 17 | public readonly langDisplayFS: number; 18 | public readonly codeFF: string | undefined; 19 | public readonly codeFS: number; 20 | public readonly codeLH: number; 21 | public readonly legendFF: string | undefined; 22 | public readonly legendFS: number; 23 | public readonly codeFooterDocsFF: string | undefined; 24 | public readonly codeFooterDocsFS: number; 25 | public readonly printOutputDocsFF: string | undefined; 26 | public readonly printOutputDocsFS: number; 27 | public readonly pyStdoutFF: string | undefined; 28 | public readonly pyStdoutFs: number; 29 | public readonly stackAndHeapHeaderFF: string | undefined; 30 | public readonly stackAndHeapHeaderFS: number; 31 | public readonly stackFrameFF: string | undefined; 32 | public readonly stackFrameFS: number; 33 | public readonly retValFS: number; 34 | public readonly stackFrameHeaderFF: string | undefined; 35 | public readonly stackFrameHeaderFS: number; 36 | public readonly heapObjectFF: string | undefined; 37 | public readonly heapObjectFS: number; 38 | public readonly typeLabelFF: string | undefined; 39 | public readonly typeLabelFS: number; 40 | public readonly light_highlightedArrow_color: string; 41 | public readonly light_highlightedStackFrame_bgColor: string; 42 | public readonly light_list_tuple_setTbl_bgColor: string; 43 | public readonly light_dict_class_instKey_bgColor: string; 44 | public readonly light_dict_class_instVal_bgColor: string; 45 | public readonly dark_highlightedArrow_color: string; 46 | public readonly dark_highlightedStackFrame_bgColor: string; 47 | public readonly dark_list_tuple_setTbl_bgColor: string; 48 | public readonly dark_dict_class_instKey_bgColor: string; 49 | public readonly dark_dict_class_instVal_bgColor: string; 50 | public readonly highContrast_highlightedArrow_color: string; 51 | public readonly highContrast_highlightedStackFrame_bgColor: string; 52 | public readonly highContrast_list_tuple_setTbl_bgColor: string; 53 | public readonly highContrast_dict_class_instKey_bgColor: string; 54 | public readonly highContrast_dict_class_instVal_bgColor: string; 55 | public readonly styles: string[]; 56 | 57 | public constructor(pythonConfig: vscode.WorkspaceConfiguration) { 58 | this.fontFamily = pythonConfig.get('fontFamliy', undefined); 59 | this.fontSize = Math.max(16, +pythonConfig.get('fontSize', NaN)); 60 | this.langDisplayFF = pythonConfig.get('langDisplay.fontFamily', undefined); 61 | this.langDisplayFS = Math.max(14, +pythonConfig.get('langDisplay.fontSize', NaN)); 62 | this.codeFF = pythonConfig.get('code.fontFamily', undefined); 63 | this.codeFS = Math.max(15, +pythonConfig.get('code.fontSize', NaN)); 64 | this.codeLH = Math.max(1, +pythonConfig.get('code.lineHeight', NaN)); 65 | this.legendFF = pythonConfig.get('legend.fontFamily', undefined); 66 | this.legendFS = Math.max(12, +pythonConfig.get('legend.fontSize', NaN)); 67 | this.codeFooterDocsFF = pythonConfig.get('codeFooterDocs.fontFamily', undefined); 68 | this.codeFooterDocsFS = Math.max(12, +pythonConfig.get('codeFooterDocs.fontSize', NaN)); 69 | this.printOutputDocsFF = pythonConfig.get('printOutputDocs.fontFamily', undefined); 70 | this.printOutputDocsFS = Math.max(12, +pythonConfig.get('printOutputDocs.fontSize', NaN)); 71 | this.pyStdoutFF = pythonConfig.get('progOutputs.fontFamily', undefined); 72 | this.pyStdoutFs = Math.max(14, +pythonConfig.get('progOutputs.fontSize', NaN)); 73 | this.stackAndHeapHeaderFF = pythonConfig.get('stackAndHeapHeader.fontFamily', undefined); 74 | this.stackAndHeapHeaderFS = Math.max(14, +pythonConfig.get('stackAndHeapHeader.fontSize', NaN)); 75 | this.stackFrameFF = pythonConfig.get('stackFrame.fontFamily', undefined); 76 | this.stackFrameFS = Math.max(14, +pythonConfig.get('stackFrame.fontSize', NaN)); 77 | this.retValFS = Math.max(12, +pythonConfig.get('retVal.fontSize', NaN)); 78 | this.stackFrameHeaderFF = pythonConfig.get('stackFrameHeder.fontFamily', undefined);; 79 | this.stackFrameHeaderFS = Math.max(14, +pythonConfig.get('stackFrameHeader.fontSize', NaN)); 80 | this.heapObjectFF = pythonConfig.get('heapObject.fontFamily', undefined); 81 | this.heapObjectFS = Math.max(14, +pythonConfig.get('heapObject.fontSize', NaN)); 82 | this.typeLabelFF = pythonConfig.get('typeLabel.fontFamily', undefined); 83 | this.typeLabelFS = Math.max(12, +pythonConfig.get('typeLabel.fontSize', NaN)); 84 | 85 | this.light_highlightedArrow_color = pythonConfig.get('light.highlightedArrow.color'); 86 | this.light_highlightedStackFrame_bgColor = pythonConfig.get('light.highlightedStackFrame.bgColor'); 87 | this.light_list_tuple_setTbl_bgColor = pythonConfig.get('light.list-tuple-setTbl.bgColor'); 88 | this.light_dict_class_instKey_bgColor = pythonConfig.get('light.dict-class-instKey.bgColor'); 89 | this.light_dict_class_instVal_bgColor = pythonConfig.get('light.dict-class-instVal.bgColor'); 90 | 91 | this.dark_highlightedArrow_color = pythonConfig.get('dark.highlightedArrow.color'); 92 | this.dark_highlightedStackFrame_bgColor = pythonConfig.get('dark.highlightedStackFrame.bgColor'); 93 | this.dark_list_tuple_setTbl_bgColor = pythonConfig.get('dark.list-tuple-setTbl.bgColor'); 94 | this.dark_dict_class_instKey_bgColor = pythonConfig.get('dark.dict-class-instKey.bgColor'); 95 | this.dark_dict_class_instVal_bgColor = pythonConfig.get('dark.dict-class-instVal.bgColor'); 96 | 97 | this.highContrast_highlightedArrow_color = pythonConfig.get('high-contrast.highlightedArrow.color'); 98 | this.highContrast_highlightedStackFrame_bgColor = pythonConfig.get('high-contrast.highlightedStackFrame.bgColor'); 99 | this.highContrast_list_tuple_setTbl_bgColor = pythonConfig.get('high-contrast.list-tuple-setTbl.bgColor'); 100 | this.highContrast_dict_class_instKey_bgColor = pythonConfig.get('high-contrast.dict-class-instKey.bgColor'); 101 | this.highContrast_dict_class_instVal_bgColor = pythonConfig.get('high-contrast.dict-class-instVal.bgColor'); 102 | this.styles = pythonConfig.get('styles', []); 103 | } 104 | 105 | public equals(otherConfig: StyleConfiguration) { 106 | for (let key in this) { 107 | if (this.hasOwnProperty(key) && key !== 'styles' && this[key] !== otherConfig[key]) { 108 | return false; 109 | } 110 | } 111 | 112 | if (this.styles.length !== otherConfig.styles.length) { 113 | return false; 114 | } 115 | 116 | for (let i = 0; i < this.styles.length; ++i) { 117 | if (this.styles[i] !== otherConfig.styles[i]) { 118 | return false; 119 | } 120 | } 121 | 122 | return true; 123 | } 124 | } 125 | 126 | class ContentConfiguration { 127 | [key: string]: any; 128 | public readonly disableHeapNesting: boolean; 129 | public readonly textualMemoryLabels: boolean; 130 | public readonly compactFuncLabels: boolean; 131 | public readonly showAllFrameLabels: boolean; 132 | public readonly hideCode: boolean; 133 | public readonly codAndNavWidth: number; 134 | 135 | public constructor(pythonConfig: vscode.WorkspaceConfiguration) { 136 | this.disableHeapNesting = !!pythonConfig.get('disableHeapNesting', false); 137 | this.textualMemoryLabels = !!pythonConfig.get('textualMemoryLabels', false); 138 | this.compactFuncLabels = !!pythonConfig.get('compactFuncLabels', false); 139 | this.showAllFrameLabels = !!pythonConfig.get('showAllFrameLabels', false); 140 | this.hideCode = !!pythonConfig.get('hideCode', false); 141 | this.codAndNavWidth = Math.max(510, +pythonConfig.get('codAndNavWidth', 510)); 142 | } 143 | 144 | public equals(otherConfig: ContentConfiguration) { 145 | for (let key in this) { 146 | if (this.hasOwnProperty(key) && this[key] !== otherConfig[key]) { 147 | return false; 148 | } 149 | } 150 | return true; 151 | } 152 | } 153 | 154 | class TraceConfiguration { 155 | [key: string]: any; 156 | public readonly cumulativeMode: boolean; 157 | public readonly allowAllModules: boolean; 158 | 159 | public readonly maxExecutedLines: number; 160 | 161 | public constructor(pythonConfig: vscode.WorkspaceConfiguration) { 162 | this.cumulativeMode = !!pythonConfig.get('cumulativeMode', false); 163 | this.allowAllModules = !!pythonConfig.get('allowAllModules', true); 164 | this.maxExecutedLines = Math.max(1000, +pythonConfig.get('maxExecutedLines', 1000)); 165 | } 166 | 167 | public equals(otherConfig: TraceConfiguration) { 168 | for (let key in this) { 169 | if (this.hasOwnProperty(key) && this[key] !== otherConfig[key]) { 170 | return false; 171 | } 172 | } 173 | return true; 174 | } 175 | } 176 | 177 | export class PythonPreviewConfiguration { 178 | public static getForResource(resouce: vscode.Uri) { 179 | return new PythonPreviewConfiguration(resouce); 180 | } 181 | 182 | public readonly styleConfig: StyleConfiguration; 183 | public readonly contentConfig: ContentConfiguration; 184 | public readonly traceConfig: TraceConfiguration; 185 | 186 | private constructor(resource: vscode.Uri) { 187 | const pythonConfig = vscode.workspace.getConfiguration('pythonPreview', resource); 188 | this.styleConfig = new StyleConfiguration(pythonConfig); 189 | this.contentConfig = new ContentConfiguration(pythonConfig); 190 | this.traceConfig = new TraceConfiguration(pythonConfig); 191 | } 192 | 193 | public equals(otherConfig: PythonPreviewConfiguration) { 194 | if (!this.styleConfig.equals(otherConfig.styleConfig)) return false; 195 | if (!this.contentConfig.equals(otherConfig.contentConfig)) return false; 196 | if (!this.traceConfig.equals(otherConfig.traceConfig)) return false; 197 | return true; 198 | } 199 | } 200 | 201 | export class PythonPreviewConfigurationManager { 202 | private readonly _previewConfigurationsForWorkspaces = new Map(); 203 | 204 | public loadAndCacheConfiguration(resource: vscode.Uri): PythonPreviewConfiguration { 205 | const config = PythonPreviewConfiguration.getForResource(resource); 206 | this._previewConfigurationsForWorkspaces.set(this.getKey(resource), config); 207 | return config; 208 | } 209 | 210 | public getConfigCacheForResource(resource: vscode.Uri): PythonPreviewConfiguration { 211 | let config = this._previewConfigurationsForWorkspaces.get(this.getKey(resource)); 212 | if (!config) { 213 | config = PythonPreviewConfiguration.getForResource(resource); 214 | this._previewConfigurationsForWorkspaces.set(this.getKey(resource), config); 215 | } 216 | return config; 217 | } 218 | 219 | public hasConfigurationChanged(resource: vscode.Uri): boolean { 220 | const key = this.getKey(resource); 221 | const currentConfig = this._previewConfigurationsForWorkspaces.get(key); 222 | const newConfig = PythonPreviewConfiguration.getForResource(resource); 223 | return !(currentConfig && currentConfig.equals(newConfig)); 224 | } 225 | 226 | private getKey(resource: vscode.Uri): string { 227 | const folder = vscode.workspace.getWorkspaceFolder(resource); 228 | return folder ? folder.uri.toString() : ''; 229 | } 230 | } -------------------------------------------------------------------------------- /src/features/previewContentProvider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from 'path'; 3 | import {Logger} from "../common/logger"; 4 | import {PythonPreviewConfiguration, PythonPreviewConfigurationManager} from "./previewConfig"; 5 | 6 | export class PythonContentProvider { 7 | constructor(private readonly _context: vscode.ExtensionContext, 8 | private readonly _logger: Logger) { 9 | } 10 | 11 | public provideTextDocumentContent(pythonDocument: vscode.TextDocument, 12 | previewConfigurationManager: PythonPreviewConfigurationManager, 13 | state?: any): string { 14 | const sourceUri = pythonDocument.uri; 15 | const config = previewConfigurationManager.loadAndCacheConfiguration(sourceUri); 16 | 17 | // Content Security Policy 18 | const nonce = new Date().getTime() + '' + new Date().getMilliseconds(); 19 | 20 | return ` 21 | 22 | 23 | 24 | 25 | 27 | ${this.getStyles(sourceUri, nonce, config)} 28 | 29 | 30 | 31 |
32 | 33 | 34 | `; 35 | } 36 | 37 | private fixHref(resource: vscode.Uri, href: string): string { 38 | if (!href) { 39 | return href; 40 | } 41 | 42 | const hrefUri = vscode.Uri.parse(href); 43 | if (['http', 'https'].indexOf(hrefUri.scheme) >= 0) { 44 | return hrefUri.toString(); 45 | } 46 | 47 | if (path.isAbsolute(href) || hrefUri.scheme === 'file') { 48 | return vscode.Uri.file(href).with({scheme: 'vscode-resource'}).toString(); 49 | } 50 | 51 | let root = vscode.workspace.getWorkspaceFolder(resource); 52 | if (root) { 53 | return vscode.Uri.file(path.join(root.uri.fsPath, href)).with({scheme: 'vscode-resource'}).toString(); 54 | } 55 | 56 | return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).with({scheme: 'vscode-resource'}).toString(); 57 | } 58 | 59 | private getSettingsOverrideStyles(nonce: string, config: PythonPreviewConfiguration): string { 60 | return ``; 201 | } 202 | 203 | private getCustomStyles(resource: vscode.Uri, config: PythonPreviewConfiguration): string { 204 | if (Array.isArray(config.styleConfig.styles)) { 205 | return config.styleConfig.styles.map(style => { 206 | return `` 207 | }).join('\n'); 208 | } 209 | return ''; 210 | } 211 | 212 | private getStyles(resource: vscode.Uri, nonce: string, config: PythonPreviewConfiguration): string { 213 | const baseStyles = ['jquery-ui.min.css', 'pytutor.common.css', 'pytutor.theme.css'] 214 | .map(item => ``) 215 | .join('\n'); 216 | 217 | return `${baseStyles} 218 | ${this.getSettingsOverrideStyles(nonce, config)} 219 | ${this.getCustomStyles(resource, config)}`; 220 | } 221 | 222 | private extensionResourcePath(assetFile: string): string { 223 | return vscode.Uri.file(this._context.asAbsolutePath(path.join('assets', assetFile))) 224 | .with({ scheme: 'vscode-resource' }) 225 | .toString(); 226 | } 227 | } -------------------------------------------------------------------------------- /src/features/previewManager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from 'path'; 3 | import {PythonPreviewConfigurationManager} from "./previewConfig"; 4 | import {PythonPreview} from "./preview"; 5 | import {PythonContentProvider} from "./previewContentProvider"; 6 | import {Logger} from "../common/logger"; 7 | import {disposeAll} from "../common/dispose"; 8 | import { IDebugServer, LaunchRequestArguments } from "../debugger/common/contracts"; 9 | import { PythonProcess } from "../debugger/pythonProcess"; 10 | import { BaseDebugClient } from "../debugger/debugClients/baseDebugClient"; 11 | import { BaseDebugServer } from "../debugger/debugServers/baseDebugServer"; 12 | import { getPythonExecutable } from "../debugger/common/utils"; 13 | import { DebuggerLauncherScriptProvider } from "../debugger/debugClients/launcherProvider"; 14 | import { LocalDebugClient } from "../debugger/debugClients/localDebugClient"; 15 | import { PythonOutput, PythonOutputStatus } from "./pythonOutput"; 16 | import { isNotInstalledError } from "../common/helpers"; 17 | 18 | export class PythonPreviewManager implements vscode.WebviewPanelSerializer { 19 | private static readonly _pythonPreviewActiveContextKey = 'pythonPreviewFocus'; 20 | 21 | private readonly _previewConfigurationManager = new PythonPreviewConfigurationManager(); 22 | private _previews: PythonPreview[] = []; 23 | private _activePreview: PythonPreview | undefined = undefined; 24 | private readonly _disposables: vscode.Disposable[] = []; 25 | 26 | private _debuggerLoaded: Promise | undefined; 27 | private _debuggerLoadedPromiseResolve!: () => void; 28 | private _pythonProcess?: PythonProcess; 29 | private _debugClient?: BaseDebugClient<{}>; 30 | private _debugServer!: BaseDebugServer; 31 | private _launchArgs!: LaunchRequestArguments; 32 | private _cachedOutputs: Map; 33 | private _lang?: string; 34 | 35 | public constructor(private readonly _context: vscode.ExtensionContext, 36 | private readonly _contentProvider: PythonContentProvider, 37 | private readonly _logger: Logger){ 38 | this._disposables.push(vscode.window.registerWebviewPanelSerializer(PythonPreview.viewtype, this)); 39 | this._cachedOutputs = new Map(); 40 | } 41 | 42 | public dispose(): void { 43 | disposeAll(this._disposables); 44 | disposeAll(this._previews); 45 | this._cachedOutputs.clear(); 46 | this.stopDebugServer(); 47 | } 48 | 49 | public refresh() { 50 | for (const preview of this._previews) { 51 | preview.initialContent(); 52 | } 53 | } 54 | 55 | public updateConfiguration() { 56 | for (const preview of this._previews) { 57 | preview.updateConfiguration(); 58 | } 59 | } 60 | 61 | public preview(resource: vscode.Uri, previewSettings: PreviewSettings): void { 62 | // 第一次预览,首先创建调试器。 63 | if (this._debuggerLoaded === undefined) { 64 | this._launchArgs = { 65 | pythonPath: 'python' 66 | }; 67 | this._debuggerLoaded = new Promise(resolve => { 68 | this._debuggerLoadedPromiseResolve = resolve; 69 | }); 70 | this.createDebugger(this._launchArgs); 71 | } 72 | // 这段代码永远找不到已存在的preview,原因是preview的列输入参数是vscode.ViewColumn.Beside或者vscode.ViewColumn.Beside!!! 73 | let preview = this.getExistingPreview(resource, previewSettings); 74 | if (preview) { 75 | preview.reveal(previewSettings.previewColumn); 76 | } else { 77 | preview = this.createNewPreview(resource, previewSettings); 78 | preview.initialContent(); 79 | } 80 | } 81 | 82 | public get activePreviewResource(): vscode.Uri | undefined { 83 | return this._activePreview && this._activePreview.resource; 84 | } 85 | 86 | public get lang() { 87 | return this._lang; 88 | } 89 | 90 | public toggleLock() { 91 | const preview = this._activePreview; 92 | if (preview) { 93 | preview.toggleLock(); 94 | // 关闭冗余的预览 95 | for (const otherPreview of this._previews) { 96 | if (otherPreview !== preview && preview.matches(otherPreview)) { 97 | otherPreview.dispose(); 98 | } 99 | } 100 | } 101 | } 102 | 103 | public async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any): Promise { 104 | 105 | const preview = await PythonPreview.receive( 106 | webviewPanel, 107 | state, 108 | this, 109 | this._context, 110 | this._cachedOutputs, 111 | this._contentProvider, 112 | this._previewConfigurationManager, 113 | this._logger); 114 | 115 | this.registerPreview(preview); 116 | } 117 | 118 | 119 | 120 | private getExistingPreview(resource: vscode.Uri, previewSettings: PreviewSettings): PythonPreview | undefined { 121 | return this._previews.find(preview => 122 | preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)); 123 | } 124 | 125 | private createNewPreview(resource: vscode.Uri, previewSettings: PreviewSettings): PythonPreview { 126 | const preview = PythonPreview.create(resource, 127 | previewSettings.previewColumn, 128 | previewSettings.locked, 129 | this, 130 | this._context, 131 | this._cachedOutputs, 132 | this._contentProvider, 133 | this._previewConfigurationManager, 134 | this._logger); 135 | 136 | this.setPreviewActiveContext(true); 137 | this._activePreview = preview; 138 | return this.registerPreview(preview); 139 | } 140 | 141 | private registerPreview(preview: PythonPreview): PythonPreview { 142 | this._previews.push(preview); 143 | 144 | preview.onDidDispose(() => { 145 | const existing = this._previews.indexOf(preview); 146 | if (existing === -1) { 147 | return; 148 | } 149 | this._previews.splice(existing, 1); 150 | if (this._activePreview === preview) { 151 | this.setPreviewActiveContext(false); 152 | this._activePreview = undefined; 153 | } 154 | if (this._previews.length === 0) { 155 | this.stopDebugServer(); 156 | this._cachedOutputs.clear(); 157 | } else { 158 | const isSameResource = this._previews.some(item => { 159 | if (item.resource.fsPath == preview.resource.fsPath) return true; 160 | }); 161 | if (!isSameResource && this._cachedOutputs.has(preview.resource.fsPath)) { 162 | this._cachedOutputs.delete(preview.resource.fsPath); 163 | } 164 | } 165 | 166 | }); 167 | 168 | preview.onDidChangeViewState(({ webviewPanel }) => { 169 | disposeAll(this._previews.filter(otherPreview => preview !== otherPreview && preview!.matches(otherPreview))); 170 | this.setPreviewActiveContext(webviewPanel.active); 171 | this._activePreview = webviewPanel.active ? preview : undefined; 172 | }); 173 | 174 | return preview; 175 | } 176 | 177 | private setPreviewActiveContext(value: boolean) { 178 | vscode.commands.executeCommand('setContext', PythonPreviewManager._pythonPreviewActiveContextKey, value); 179 | } 180 | 181 | public createDebugger(args: LaunchRequestArguments): void { 182 | try { 183 | args.pythonPath = getPythonExecutable(args.pythonPath); 184 | } catch (ex) { } 185 | 186 | this._launchArgs = args; 187 | let launchScriptProvider = new DebuggerLauncherScriptProvider(); 188 | this._debugClient = new LocalDebugClient(args, launchScriptProvider, this, this._logger); 189 | 190 | const that = this; 191 | this.startDebugServer().then(debugServer => { 192 | this._logger.info(`Started Debug Server. It is listening port - ${debugServer.port}`); 193 | return that._debugClient!.launchApplicationToDebug(debugServer); 194 | }).catch(error => { 195 | let errorMsg = typeof error === 'string' ? error : ((error.message && error.message.length > 0) ? error.message : error); 196 | if (isNotInstalledError(error)) { 197 | errorMsg = `Failed to launch the Python Process, please valiate the path '${this._launchArgs.pythonPath}'`; 198 | } 199 | vscode.window.showErrorMessage(errorMsg); 200 | this._logger.error('Starting Debugger with error.', errorMsg); 201 | this.dispose(); 202 | }); 203 | } 204 | 205 | private initializeEventHandlers() { 206 | const pythonProcess = this._pythonProcess!; 207 | pythonProcess.on('processLoaded', pythonVersion => this.onPythonProcessLoaded(pythonVersion)); 208 | pythonProcess.on('output', (fileName, output) => this.onDebuggerOutput(fileName, output)); 209 | pythonProcess.on('detach', () => this.onDetachDebugger()); 210 | 211 | this._debugServer.on('detach', () => this.onDetachDebugger()); 212 | } 213 | 214 | public postMessageToDebugger(fileName: string, code: string) { 215 | if (this._debuggerLoaded === undefined) { 216 | this._launchArgs = { 217 | pythonPath: 'python' 218 | }; 219 | this._debuggerLoaded = new Promise(resolve => { 220 | this._debuggerLoadedPromiseResolve = resolve; 221 | }); 222 | this.createDebugger(this._launchArgs); 223 | } 224 | this._debuggerLoaded.then(() => { 225 | let output = this._cachedOutputs.get(fileName); 226 | // 第一次传送数据,则直接传送 227 | if (!output) { 228 | this._cachedOutputs.set(fileName, new PythonOutput()); 229 | this.sendMessage(fileName, code); 230 | output.status = PythonOutputStatus.Initialized; 231 | } else { 232 | // 如果是之后的传送,则设置定时器 233 | clearTimeout(output.throttleTimer); 234 | output.throttleTimer = setTimeout(() => { 235 | this.sendMessage(fileName, code); 236 | output.status = PythonOutputStatus.Processing; 237 | output.throttleTimer = undefined; 238 | }, 300); 239 | } 240 | }); 241 | } 242 | 243 | private sendMessage(fileName: string, code: string) { 244 | const config = this._previewConfigurationManager.getConfigCacheForResource(vscode.Uri.file(fileName)); 245 | const folder = PythonPreviewManager.getWorkspacePathOrPathRealtiveToFile(fileName); 246 | const cumulativeMode = config.traceConfig.cumulativeMode; 247 | const allowAllModules = config.traceConfig.allowAllModules; 248 | const maxExecutedLines = config.traceConfig.maxExecutedLines; 249 | this._logger.info('Sending executed code to debugger'); 250 | this._pythonProcess!.sendExecutableText(folder, fileName, code, cumulativeMode, allowAllModules, maxExecutedLines); 251 | } 252 | 253 | private static getWorkspacePathOrPathRealtiveToFile(fileName: string) { 254 | let root = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(fileName)); 255 | if (root) { 256 | return root.uri.fsPath; 257 | } 258 | return path.dirname(fileName); 259 | } 260 | 261 | private onPythonProcessLoaded(pythonVersion: string) { 262 | this._logger.info('Python Process loaded'); 263 | this._lang = `Python ${pythonVersion}`; 264 | this._debuggerLoadedPromiseResolve(); 265 | } 266 | 267 | private onDebuggerOutput(fileName: string, output: string) { 268 | const data = JSON.parse(output); 269 | let cacheOutput = this._cachedOutputs.get(fileName)!; 270 | cacheOutput.status = PythonOutputStatus.Prcoessed; 271 | cacheOutput.trace = data; 272 | this._previews.forEach(item => { 273 | if (item.isPreviewOf(vscode.Uri.file(fileName))) { 274 | if (item.visibale) { 275 | item.updateContent(); 276 | } 277 | } 278 | }); 279 | } 280 | 281 | public onDetachDebugger() { 282 | this.stopDebugServer(); 283 | } 284 | 285 | private startDebugServer(): Promise { 286 | this._pythonProcess = new PythonProcess(0, ''); 287 | this._debugServer = this._debugClient!.createDebugServer(this._pythonProcess); 288 | this.initializeEventHandlers(); 289 | this._logger.info('Starting Debug Server'); 290 | return this._debugServer.start(); 291 | } 292 | 293 | private stopDebugServer() { 294 | if (this._debugClient) { 295 | this._debugClient!.stop(); 296 | this._debugClient = undefined; 297 | } 298 | if (this._pythonProcess) { 299 | this._pythonProcess!.kill(); 300 | this._pythonProcess = undefined; 301 | } 302 | this._debuggerLoaded = undefined; 303 | if (this._lang) { 304 | this._lang = undefined; 305 | this._logger.info('Debugger exited'); 306 | } 307 | } 308 | 309 | } 310 | 311 | export interface PreviewSettings { 312 | readonly resourceColumn: vscode.ViewColumn; 313 | readonly previewColumn: vscode.ViewColumn; 314 | readonly locked: boolean; 315 | } -------------------------------------------------------------------------------- /src/features/pythonOutput.ts: -------------------------------------------------------------------------------- 1 | export enum PythonOutputStatus { 2 | Initialized = 1, 3 | Processing = 2, 4 | Prcoessed = 3 5 | } 6 | 7 | export class PythonOutput { 8 | private _status: PythonOutputStatus; 9 | 10 | public constructor(private _trace?: any, private _throttleTimer?: NodeJS.Timer) { 11 | this._status = PythonOutputStatus.Initialized; 12 | } 13 | 14 | public get trace() { 15 | return this._trace; 16 | } 17 | 18 | public set trace(value) { 19 | this._trace = value; 20 | } 21 | 22 | public get status() { 23 | return this._status; 24 | } 25 | 26 | public set status(value) { 27 | this._status = value; 28 | } 29 | 30 | public get throttleTimer() { 31 | return this._throttleTimer; 32 | } 33 | 34 | public set throttleTimer(value) { 35 | this._throttleTimer = value; 36 | } 37 | } -------------------------------------------------------------------------------- /src/test/common/net/socketStream.test.ts: -------------------------------------------------------------------------------- 1 | import * as net from 'net'; 2 | import * as assert from 'assert'; 3 | 4 | import { SocketStream } from '../../../common/net/socket/socketStream'; 5 | const uint64be = require('uint64be'); 6 | 7 | class MockSocket { 8 | private _rawDataWritten: any; 9 | constructor(private _data: string = '') { } 10 | 11 | public get dataWritten(): string { 12 | return this._data; 13 | } 14 | 15 | public get rawDataWritten(): any { 16 | return this._rawDataWritten; 17 | } 18 | 19 | write(data: any) { 20 | this._data = data + ''; 21 | this._rawDataWritten = data; 22 | } 23 | } 24 | 25 | suite('SocketStream', () => { 26 | test('Read Byte', done => { 27 | let buffer = Buffer.from('P'); 28 | const byteValue = buffer[0]; 29 | const socket = new MockSocket(); 30 | const stream = new SocketStream((socket as any) as net.Socket, buffer); 31 | 32 | assert.equal(stream.readByte(), byteValue); 33 | done(); 34 | }); 35 | 36 | test('Read Int32', done => { 37 | const max = 2147483648; 38 | const socket = new MockSocket(); 39 | let buffer = uint64be.encode(max); 40 | const stream = new SocketStream((socket as any) as net.Socket, buffer); 41 | 42 | assert.equal(stream.readInt32(), max); 43 | done(); 44 | }); 45 | 46 | test('Read Int64', done => { 47 | const max = 9223372036854775807; 48 | const socket = new MockSocket(); 49 | let buffer = uint64be.encode(max); 50 | const stream = new SocketStream((socket as any) as net.Socket, buffer); 51 | 52 | assert.equal(stream.readInt64(), max); 53 | done(); 54 | }); 55 | 56 | test('Read Ascii String', done => { 57 | const message = 'Hello World'; 58 | const socket = new MockSocket(); 59 | let buffer = Buffer.concat([Buffer.from('A'), uint64be.encode(message.length), Buffer.from(message)]); 60 | const stream = new SocketStream((socket as any) as net.Socket, buffer); 61 | 62 | assert.equal(stream.readString(), message); 63 | done(); 64 | }) 65 | 66 | test('Read Unicode String', done => { 67 | const message = 'Hello World - Функция проверки ИНН и КПП - 说明'; 68 | const socket = new MockSocket(); 69 | const stringBuffer = Buffer.from(message); 70 | let buffer = Buffer.concat([Buffer.from('U'), uint64be.encode(stringBuffer.byteLength), stringBuffer]); 71 | const stream = new SocketStream((socket as any) as net.Socket, buffer); 72 | 73 | assert.equal(stream.readString(), message); 74 | done(); 75 | }); 76 | 77 | 78 | }) -------------------------------------------------------------------------------- /src/test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | import * as vscode from 'vscode'; 12 | import * as myExtension from '../extension'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", () => { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", () => { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | import * as testRunner from 'vscode/lib/testrunner'; 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "experimentalDecorators": true 12 | /* Strict Type-Checking Option */ 13 | // "strict": true, /* enable all strict type-checking options */ 14 | /* Additional Checks */ 15 | // "noUnusedLocals": true /* Report errors on unused locals. */ 16 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 17 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 18 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | ".vscode-test", 23 | "out", 24 | "preview-src" 25 | ] 26 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ], 12 | "triple-equals": true 13 | }, 14 | "defaultSeverity": "warning" 15 | } --------------------------------------------------------------------------------