├── .gitignore ├── .idea ├── .name ├── codeStyleSettings.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── intellij-plugin.iml ├── libraries │ ├── com_google_code_gson_gson_Release.xml │ └── com_mashape_unirest_unirest_java_Release.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── intellij-plugin.iml ├── resources ├── META-INF │ └── plugin.xml ├── code-mirror │ ├── codemirror-darcula.css │ ├── codemirror-old-darcula.css │ ├── codemirror-old.css │ ├── codemirror.css │ ├── codemirror.js │ ├── colorize.js │ ├── javascript.js │ ├── runmode.js │ └── template.html ├── icons │ ├── download_blue.png │ ├── stepik_logo_green.png │ └── stepik_logotype_13x13-2.png ├── python.js └── style │ ├── javaFXBrowserDarcula.css │ ├── javaFXBrowserDarculaScrollBar.css │ └── javaFXBrowserScrollBar.css └── src └── org └── stepik └── plugin ├── actions ├── mainMenu │ ├── Help.java │ ├── MainMenuAction.java │ ├── NegTranslator.java │ ├── PrintMapInfo.java │ ├── RefreshMap.java │ ├── RefreshToken.java │ ├── SignIn.java │ ├── SomeAction.java │ ├── UpdateCourse.java │ └── WhoAmI.java └── popupMenu │ ├── DownloadLastSubmission.java │ ├── GetIdStep.java │ ├── GetStepStatus.java │ ├── PopupMenuAction.java │ └── SendFile.java ├── components ├── MyModuleComponent.java └── MyProjectComponent.java ├── icons └── PluginIcons.java ├── modules ├── Attempt.java ├── Course.java ├── CourseInfo.java ├── Lesson.java ├── Section.java ├── Step.java ├── StepInfo.java ├── Submission.java ├── TokenInfo.java └── Unit.java ├── projectWizard ├── StepikModuleBuilder.java ├── StepikModuleType.java ├── StepikModuleWizardStep.form ├── StepikModuleWizardStep.java └── StepikProjectStructureDetector.java ├── stepikConnector └── StepikConnector.java ├── storages ├── ActionVisibleProperties.java ├── CourseDefinitionStorage.java ├── StepikApplicationStorage.java └── StudentStorage.java ├── toolWindow ├── JavaStudyPluginConfigurator.java ├── StudyBasePluginConfigurator.java ├── StudyBrowserWindow.java ├── StudyCondition.java ├── StudyEditor.java ├── StudyEditorFactoryListener.java ├── StudyJavaFxToolWindow.java ├── StudyPluginConfigurator.java ├── StudySwingToolWindow.java ├── StudyToolWindow.java ├── StudyToolWindowFactory.java └── StudyUtils.java └── utils ├── MyLogger.java ├── NotificationTemplates.java ├── NotificationUtils.java ├── Utils.java └── YaTranslator.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | out/ 14 | .idea/workspace.xml 15 | .idea/tasks.xml 16 | intellij-plugin.zip 17 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | intellij-plugin -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 13 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/intellij-plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/libraries/com_google_code_gson_gson_Release.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/libraries/com_mashape_unirest_unirest_java_Release.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository is outdated 2 | Current repository: [intellij-plugins](https://github.com/StepicOrg/intellij-plugins)
3 | 4 | # intellij-plugin 0.6.8 5 | 6 | To work with plugin you must to create a project of Stepik type. 7 | You need to enter login and password your Stepik account. 8 | And then input course URL or course number. 9 | After twenty seconds course will be built. 10 | 11 | To send task click the left mouse button on file. In the shortcut menu choose "Send step". 12 | In the same way you can to download last submissions and get the status of step. 13 | 14 | 15 | ## Features 16 | * **Sign in** — you can input login and password, and then you get OAuth2 token. 17 | * **Who am i** — return `user_name`. 18 | * **Download Latest Submission** — download your latest submission this step from site. 19 | * **Check Step Status** — return a step status and status of last submission posted from IDE. 20 | * **Submit Solution** — send this step to platform. 21 | 22 | ## Install 23 | 0. Download and install IntelliJ IDEA 2016 (https://www.jetbrains.com/idea/) 24 | 1. Download latest version of the plugin from https://drive.google.com/open?id=0B3r_Au4BpPbweXJhYWpYWTdkZnc (intellij-plugin.zip) or build it yourself from the source code. 25 | 2. Open IntelliJ IDEA 26 | 3. Menu IntelliJ IDEA > Preferences > Plugins > Install plugin from disk... > intellij-plugin.zip 27 | 4. Restart IDEA 28 | 29 | ## Usage 30 | 1. File > New > Project > Stepik 31 | 2. Enter your login/password and a course link (in a form of https://stepik.org/course/some-course-name-ID/ or just ID) 32 | 33 | ## Build 34 | 1. In order to build this project, you have to setting up an development environment (IntelliJ Platform SDK) as described here: http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/setting_up_environment.html 35 | 2. Clone project, e.g. using `git clone https://github.com/StepicOrg/intellij-plugin.git`. 36 | 3. Deploy the plugin as described here: http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/deploying_plugin.html 37 | -------------------------------------------------------------------------------- /intellij-plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | stepik.submmit.plugin.id 3 | Stepik Java submitter -beta- 4 | 0.6.8 5 | Stepik 6 | 7 | 9 | To use this plugin you have to have Stepik account and follow the course.
10 |
11 | It used Yandex.Translator to translate lesson names on Russian to package names on English.
12 | ]]>
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 38 | 39 | 41 | 42 | 44 | 45 | 48 | 49 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 98 | 99 | 100 | 101 | 102 | 104 | 105 | 106 | 107 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 131 | 132 | 133 | 135 | 136 | 137 | 141 | 142 | 143 | 144 | 146 | 147 | 148 | 149 | 153 | 154 | 155 | 156 | 158 | 159 | 160 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | org.stepik.plugin.components.MyModuleComponent 169 | 170 | 171 | 172 | 173 | org.stepik.plugin.components.MyProjectComponent 174 | 175 | 176 |
177 | -------------------------------------------------------------------------------- /resources/code-mirror/codemirror-darcula.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2014 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | body { 18 | margin-left: 20px; 19 | font-family: Helvetica, sans-serif; 20 | } 21 | 22 | code { 23 | font-family: "Source Code Pro", monospace; 24 | } 25 | 26 | /* BASICS */ 27 | .task-help pre code { 28 | background-color: #f7f7f7; 29 | white-space: pre-wrap; 30 | margin-bottom: 10px; 31 | padding: 5px 25px 5px 15px; 32 | display: inline-block; 33 | margin-right: 21px; 34 | } 35 | 36 | .CodeMirror { 37 | /* Set height, width, borders, and global font properties here */ 38 | font-family: monospace; 39 | height: 300px; 40 | color: black; 41 | } 42 | 43 | /* PADDING */ 44 | 45 | .CodeMirror-lines { 46 | padding: 4px 0; /* Vertical padding around content */ 47 | } 48 | .CodeMirror pre { 49 | padding: 0 4px; /* Horizontal padding of content */ 50 | } 51 | 52 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 53 | background-color: white; /* The little square between H and V scrollbars */ 54 | } 55 | 56 | /* GUTTER */ 57 | 58 | .CodeMirror-gutters { 59 | border-right: 1px solid #ddd; 60 | background-color: #f7f7f7; 61 | white-space: nowrap; 62 | } 63 | .CodeMirror-linenumbers {} 64 | .CodeMirror-linenumber { 65 | padding: 0 3px 0 5px; 66 | min-width: 20px; 67 | text-align: right; 68 | color: #999; 69 | white-space: nowrap; 70 | } 71 | 72 | .CodeMirror-guttermarker { color: black; } 73 | .CodeMirror-guttermarker-subtle { color: #999; } 74 | 75 | /* CURSOR */ 76 | 77 | .CodeMirror-cursor { 78 | border-left: 1px solid black; 79 | border-right: none; 80 | width: 0; 81 | } 82 | /* Shown when moving in bi-directional text */ 83 | .CodeMirror div.CodeMirror-secondarycursor { 84 | border-left: 1px solid silver; 85 | } 86 | .cm-fat-cursor .CodeMirror-cursor { 87 | width: auto; 88 | border: 0; 89 | background: #7e7; 90 | } 91 | .cm-fat-cursor div.CodeMirror-cursors { 92 | z-index: 1; 93 | } 94 | 95 | .cm-animate-fat-cursor { 96 | width: auto; 97 | border: 0; 98 | -webkit-animation: blink 1.06s steps(1) infinite; 99 | -moz-animation: blink 1.06s steps(1) infinite; 100 | animation: blink 1.06s steps(1) infinite; 101 | background-color: #7e7; 102 | } 103 | @-moz-keyframes blink { 104 | 0% {} 105 | 50% { background-color: transparent; } 106 | 100% {} 107 | } 108 | @-webkit-keyframes blink { 109 | 0% {} 110 | 50% { background-color: transparent; } 111 | 100% {} 112 | } 113 | @keyframes blink { 114 | 0% {} 115 | 50% { background-color: transparent; } 116 | 100% {} 117 | } 118 | 119 | /* Can style cursor different in overwrite (non-insert) mode */ 120 | .CodeMirror-overwrite .CodeMirror-cursor {} 121 | 122 | .cm-tab { display: inline-block; text-decoration: inherit; } 123 | 124 | .CodeMirror-ruler { 125 | border-left: 1px solid #ccc; 126 | position: absolute; 127 | } 128 | 129 | /* DEFAULT THEME */ 130 | 131 | .cm-s-default .cm-header {color: #589df6;} 132 | .cm-s-default .cm-quote {color: #6a864c;} 133 | .cm-negative {color: #d44;} 134 | .cm-positive {color: #6a864c;} 135 | .cm-header, .cm-strong {font-weight: bold;} 136 | .cm-em {font-style: italic;} 137 | .cm-link {text-decoration: underline;} 138 | .cm-strikethrough {text-decoration: line-through;} 139 | 140 | .cm-s-default .cm-keyword {color: #708;} 141 | .cm-s-default .cm-atom {color: #219;} 142 | .cm-s-default .cm-number {color: #164;} 143 | .cm-s-default .cm-def {color: #589df6;} 144 | .cm-s-default .cm-variable, 145 | .cm-s-default .cm-punctuation, 146 | .cm-s-default .cm-property, 147 | .cm-s-default .cm-operator {} 148 | .cm-s-default .cm-variable-2 {color: #05a;} 149 | .cm-s-default .cm-variable-3 {color: #085;} 150 | .cm-s-default .cm-comment {color: #a50;} 151 | .cm-s-default .cm-string {color: #a11;} 152 | .cm-s-default .cm-string-2 {color: #f50;} 153 | .cm-s-default .cm-meta {color: #555;} 154 | .cm-s-default .cm-qualifier {color: #555;} 155 | .cm-s-default .cm-builtin {color: #30a;} 156 | .cm-s-default .cm-bracket {color: #997;} 157 | .cm-s-default .cm-tag {color: #170;} 158 | .cm-s-default .cm-attribute {color: #589df6;} 159 | .cm-s-default .cm-hr {color: #999;} 160 | .cm-s-default .cm-link {color: #589df6;} 161 | 162 | .cm-s-default .cm-error {color: #f00;} 163 | .cm-invalidchar {color: #f00;} 164 | 165 | .CodeMirror-composing { border-bottom: 2px solid; } 166 | 167 | /* Default styles for common addons */ 168 | 169 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 170 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 171 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 172 | .CodeMirror-activeline-background {background: #e8f2ff;} 173 | 174 | /* STOP */ 175 | 176 | /* The rest of this file contains styles related to the mechanics of 177 | the editor. You probably shouldn't touch them. */ 178 | 179 | .CodeMirror { 180 | position: relative; 181 | overflow: hidden; 182 | background: white; 183 | } 184 | 185 | .CodeMirror-scroll { 186 | overflow: scroll !important; /* Things will break if this is overridden */ 187 | /* 30px is the magic margin used to hide the element's real scrollbars */ 188 | /* See overflow: hidden in .CodeMirror */ 189 | margin-bottom: -30px; margin-right: -30px; 190 | padding-bottom: 30px; 191 | height: 100%; 192 | outline: none; /* Prevent dragging from highlighting the element */ 193 | position: relative; 194 | } 195 | .CodeMirror-sizer { 196 | position: relative; 197 | border-right: 30px solid transparent; 198 | } 199 | 200 | /* The fake, visible scrollbars. Used to force redraw during scrolling 201 | before actuall scrolling happens, thus preventing shaking and 202 | flickering artifacts. */ 203 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 204 | position: absolute; 205 | z-index: 6; 206 | display: none; 207 | } 208 | .CodeMirror-vscrollbar { 209 | right: 0; top: 0; 210 | overflow-x: hidden; 211 | overflow-y: scroll; 212 | } 213 | .CodeMirror-hscrollbar { 214 | bottom: 0; left: 0; 215 | overflow-y: hidden; 216 | overflow-x: scroll; 217 | } 218 | .CodeMirror-scrollbar-filler { 219 | right: 0; bottom: 0; 220 | } 221 | .CodeMirror-gutter-filler { 222 | left: 0; bottom: 0; 223 | } 224 | 225 | .CodeMirror-gutters { 226 | position: absolute; left: 0; top: 0; 227 | z-index: 3; 228 | } 229 | .CodeMirror-gutter { 230 | white-space: normal; 231 | height: 100%; 232 | display: inline-block; 233 | margin-bottom: -30px; 234 | /* Hack to make IE7 behave */ 235 | *zoom:1; 236 | *display:inline; 237 | } 238 | .CodeMirror-gutter-wrapper { 239 | position: absolute; 240 | z-index: 4; 241 | background: none !important; 242 | border: none !important; 243 | } 244 | .CodeMirror-gutter-background { 245 | position: absolute; 246 | top: 0; bottom: 0; 247 | z-index: 4; 248 | } 249 | .CodeMirror-gutter-elt { 250 | position: absolute; 251 | cursor: default; 252 | z-index: 4; 253 | } 254 | .CodeMirror-gutter-wrapper { 255 | -webkit-user-select: none; 256 | -moz-user-select: none; 257 | user-select: none; 258 | } 259 | 260 | .CodeMirror-lines { 261 | cursor: text; 262 | min-height: 1px; /* prevents collapsing before first draw */ 263 | } 264 | .CodeMirror pre { 265 | /* Reset some styles that the rest of the page might have set */ 266 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 267 | border-width: 0; 268 | background: transparent; 269 | font-family: inherit; 270 | font-size: inherit; 271 | margin: 0; 272 | white-space: pre; 273 | word-wrap: normal; 274 | line-height: inherit; 275 | color: inherit; 276 | z-index: 2; 277 | position: relative; 278 | overflow: visible; 279 | -webkit-tap-highlight-color: transparent; 280 | } 281 | .CodeMirror-wrap pre { 282 | word-wrap: break-word; 283 | white-space: pre-wrap; 284 | word-break: normal; 285 | } 286 | 287 | .CodeMirror-linebackground { 288 | position: absolute; 289 | left: 0; right: 0; top: 0; bottom: 0; 290 | z-index: 0; 291 | } 292 | 293 | .CodeMirror-linewidget { 294 | position: relative; 295 | z-index: 2; 296 | overflow: auto; 297 | } 298 | 299 | .CodeMirror-widget {} 300 | 301 | .CodeMirror-code { 302 | outline: none; 303 | } 304 | 305 | /* Force content-box sizing for the elements where we expect it */ 306 | .CodeMirror-scroll, 307 | .CodeMirror-sizer, 308 | .CodeMirror-gutter, 309 | .CodeMirror-gutters, 310 | .CodeMirror-linenumber { 311 | -moz-box-sizing: content-box; 312 | box-sizing: content-box; 313 | } 314 | 315 | .CodeMirror-measure { 316 | position: absolute; 317 | width: 100%; 318 | height: 0; 319 | overflow: hidden; 320 | visibility: hidden; 321 | } 322 | 323 | .CodeMirror-cursor { position: absolute; } 324 | .CodeMirror-measure pre { position: static; } 325 | 326 | div.CodeMirror-cursors { 327 | visibility: hidden; 328 | position: relative; 329 | z-index: 3; 330 | } 331 | div.CodeMirror-dragcursors { 332 | visibility: visible; 333 | } 334 | 335 | .CodeMirror-focused div.CodeMirror-cursors { 336 | visibility: visible; 337 | } 338 | 339 | .CodeMirror-selected { background: #d9d9d9; } 340 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 341 | .CodeMirror-crosshair { cursor: crosshair; } 342 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 343 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 344 | 345 | .cm-searching { 346 | background: #ffa; 347 | background: rgba(255, 255, 0, .4); 348 | } 349 | 350 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 351 | .CodeMirror span { *vertical-align: text-bottom; } 352 | 353 | /* Used to force a border model for a node */ 354 | .cm-force-border { padding-right: .1px; } 355 | 356 | @media print { 357 | /* Hide the cursor when printing */ 358 | .CodeMirror div.CodeMirror-cursors { 359 | visibility: hidden; 360 | } 361 | } 362 | 363 | /* See issue #2901 */ 364 | .cm-tab-wrap-hack:after { content: ''; } 365 | 366 | /* Help users use markselection to safely style text background */ 367 | span.CodeMirror-selectedtext { background: none; } -------------------------------------------------------------------------------- /resources/code-mirror/codemirror-old-darcula.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2014 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .cm-s-default span.cm-keyword { 18 | color: #b07732; 19 | font-weight: bold; 20 | } 21 | 22 | .cm-s-default span.cm-softKeyword { 23 | color: #b07732; 24 | font-weight: bold; 25 | } 26 | 27 | .cm-s-default span.cm-atom { 28 | color: #b07732; 29 | font-weight: bold; 30 | } 31 | 32 | .cm-s-default span.cm-number { 33 | color: #b07732; 34 | } 35 | 36 | .cm-s-default span.cm-string { 37 | color: #588652; 38 | font-weight: bold; 39 | } 40 | 41 | .cm-s-default span.cm-def { 42 | color: #dddddd; 43 | } 44 | 45 | .cm-s-default span.cm-variable { 46 | color: #dddddd; 47 | } 48 | 49 | .cm-s-default span.cm-variable-2 { 50 | color: #b07732; 51 | } 52 | 53 | .cm-s-default span.cm-variable-3 { 54 | color: #dddddd; 55 | } 56 | 57 | .cm-s-default span.cm-property { 58 | color: #dddddd; 59 | } 60 | 61 | .cm-s-default span.cm-operator { 62 | color: #dddddd; 63 | } 64 | 65 | .cm-s-default span.cm-comment { 66 | color: #7f7f7f; 67 | } 68 | 69 | .cm-s-default span.cm-string-2 { 70 | color: #f50; 71 | } 72 | 73 | .cm-s-default span.cm-meta { 74 | color: #ba9d2a; 75 | } 76 | 77 | .cm-s-default span.cm-error { 78 | color: #944b5e; 79 | } 80 | 81 | .cm-s-default span.cm-qualifier { 82 | color: #555; 83 | } 84 | 85 | .cm-s-default span.cm-builtin { 86 | color: #30a; 87 | } 88 | 89 | .cm-s-default span.cm-bracket { 90 | color: #cc7; 91 | } 92 | 93 | .cm-s-default span.cm-tag { 94 | color: #ba9d2a; 95 | } 96 | 97 | .cm-s-default span.cm-attribute { 98 | color: #589df6; 99 | } 100 | 101 | .cm-s-default span.cm-header { 102 | color: #a0a; 103 | } 104 | 105 | .cm-s-default span.cm-quote { 106 | color: #6a7846; 107 | } 108 | 109 | .cm-s-default span.cm-hr { 110 | color: #999; 111 | } 112 | 113 | .cm-s-default span.cm-link { 114 | color: #589df6; 115 | } 116 | 117 | span.cm-header, span.cm-strong { 118 | font-weight: bold; 119 | } 120 | 121 | span.cm-em { 122 | font-style: italic; 123 | } 124 | 125 | span.cm-emstrong { 126 | font-style: italic; 127 | font-weight: bold; 128 | } 129 | 130 | span.cm-link { 131 | text-decoration: underline; 132 | } 133 | 134 | div.CodeMirror span.CodeMirror-matchingbracket { 135 | /*color: #0000ff;*/ 136 | /*border: 1px #0000ff solid;*/ 137 | /*background-color: #FFE87C;*/ 138 | background-color: #e4e4ff; 139 | } 140 | 141 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 142 | color: #b76b5e; 143 | } 144 | 145 | span.bullet { 146 | padding-right: 5px; 147 | } -------------------------------------------------------------------------------- /resources/code-mirror/codemirror-old.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2014 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .cm-s-default span.cm-keyword { 18 | color: #000080; 19 | font-weight: bold; 20 | } 21 | 22 | .cm-s-default span.cm-softKeyword { 23 | color: #336699; 24 | font-weight: bold; 25 | } 26 | 27 | .cm-s-default span.cm-atom { 28 | color: #336699; 29 | font-weight: bold; 30 | } 31 | 32 | .cm-s-default span.cm-number { 33 | color: #0000ff; 34 | } 35 | 36 | .cm-s-default span.cm-string { 37 | color: #008000; 38 | font-weight: bold; 39 | } 40 | 41 | .cm-s-default span.cm-def { 42 | color: black; 43 | } 44 | 45 | .cm-s-default span.cm-variable { 46 | color: black; 47 | } 48 | 49 | .cm-s-default span.cm-variable-2 { 50 | color: #05a; 51 | } 52 | 53 | .cm-s-default span.cm-variable-3 { 54 | color: black; 55 | } 56 | 57 | .cm-s-default span.cm-property { 58 | color: black; 59 | } 60 | 61 | .cm-s-default span.cm-operator { 62 | color: black; 63 | } 64 | 65 | .cm-s-default span.cm-comment { 66 | color: #a50; 67 | } 68 | 69 | .cm-s-default span.cm-string-2 { 70 | color: #f50; 71 | } 72 | 73 | .cm-s-default span.cm-meta { 74 | color: #008000; 75 | } 76 | 77 | .cm-s-default span.cm-error { 78 | color: #f00; 79 | } 80 | 81 | .cm-s-default span.cm-qualifier { 82 | color: #555; 83 | } 84 | 85 | .cm-s-default span.cm-builtin { 86 | color: #30a; 87 | } 88 | 89 | .cm-s-default span.cm-bracket { 90 | color: #cc7; 91 | } 92 | 93 | .cm-s-default span.cm-tag { 94 | color: #170; 95 | } 96 | 97 | .cm-s-default span.cm-attribute { 98 | color: #00c; 99 | } 100 | 101 | .cm-s-default span.cm-header { 102 | color: #a0a; 103 | } 104 | 105 | .cm-s-default span.cm-quote { 106 | color: #090; 107 | } 108 | 109 | .cm-s-default span.cm-hr { 110 | color: #999; 111 | } 112 | 113 | .cm-s-default span.cm-link { 114 | color: #00c; 115 | } 116 | 117 | span.cm-header, span.cm-strong { 118 | font-weight: bold; 119 | } 120 | 121 | span.cm-em { 122 | font-style: italic; 123 | } 124 | 125 | span.cm-emstrong { 126 | font-style: italic; 127 | font-weight: bold; 128 | } 129 | 130 | span.cm-link { 131 | text-decoration: underline; 132 | } 133 | 134 | div.CodeMirror span.CodeMirror-matchingbracket { 135 | /*color: #0000ff;*/ 136 | /*border: 1px #0000ff solid;*/ 137 | /*background-color: #FFE87C;*/ 138 | background-color: #e4e4ff; 139 | } 140 | 141 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 142 | color: #f22; 143 | } 144 | 145 | span.bullet { 146 | padding-right: 5px; 147 | } -------------------------------------------------------------------------------- /resources/code-mirror/codemirror.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-left: 20px; 3 | font-family: Helvetica, sans-serif; 4 | color: #4f5254; 5 | } 6 | 7 | code { 8 | font-family: "Source Code Pro", monospace; 9 | } 10 | /* BASICS */ 11 | .task-help pre code { 12 | background-color: #f7f7f7; 13 | white-space: pre-wrap; 14 | margin-bottom: 10px; 15 | padding: 5px 25px 5px 15px; 16 | display: inline-block; 17 | margin-right: 21px; 18 | } 19 | 20 | .CodeMirror { 21 | /* Set height, width, borders, and global font properties here */ 22 | font-family: monospace; 23 | height: 300px; 24 | color: black; 25 | } 26 | 27 | /* PADDING */ 28 | 29 | .CodeMirror-lines { 30 | padding: 4px 0; /* Vertical padding around content */ 31 | } 32 | .CodeMirror pre { 33 | padding: 0 4px; /* Horizontal padding of content */ 34 | } 35 | 36 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 37 | background-color: white; /* The little square between H and V scrollbars */ 38 | } 39 | 40 | /* GUTTER */ 41 | 42 | .CodeMirror-gutters { 43 | border-right: 1px solid #ddd; 44 | background-color: #f7f7f7; 45 | white-space: nowrap; 46 | } 47 | .CodeMirror-linenumbers {} 48 | .CodeMirror-linenumber { 49 | padding: 0 3px 0 5px; 50 | min-width: 20px; 51 | text-align: right; 52 | color: #999; 53 | white-space: nowrap; 54 | } 55 | 56 | .CodeMirror-guttermarker { color: black; } 57 | .CodeMirror-guttermarker-subtle { color: #999; } 58 | 59 | /* CURSOR */ 60 | 61 | .CodeMirror-cursor { 62 | border-left: 1px solid black; 63 | border-right: none; 64 | width: 0; 65 | } 66 | /* Shown when moving in bi-directional text */ 67 | .CodeMirror div.CodeMirror-secondarycursor { 68 | border-left: 1px solid silver; 69 | } 70 | .cm-fat-cursor .CodeMirror-cursor { 71 | width: auto; 72 | border: 0; 73 | background: #7e7; 74 | } 75 | .cm-fat-cursor div.CodeMirror-cursors { 76 | z-index: 1; 77 | } 78 | 79 | .cm-animate-fat-cursor { 80 | width: auto; 81 | border: 0; 82 | -webkit-animation: blink 1.06s steps(1) infinite; 83 | -moz-animation: blink 1.06s steps(1) infinite; 84 | animation: blink 1.06s steps(1) infinite; 85 | background-color: #7e7; 86 | } 87 | @-moz-keyframes blink { 88 | 0% {} 89 | 50% { background-color: transparent; } 90 | 100% {} 91 | } 92 | @-webkit-keyframes blink { 93 | 0% {} 94 | 50% { background-color: transparent; } 95 | 100% {} 96 | } 97 | @keyframes blink { 98 | 0% {} 99 | 50% { background-color: transparent; } 100 | 100% {} 101 | } 102 | 103 | /* Can style cursor different in overwrite (non-insert) mode */ 104 | .CodeMirror-overwrite .CodeMirror-cursor {} 105 | 106 | .cm-tab { display: inline-block; text-decoration: inherit; } 107 | 108 | .CodeMirror-ruler { 109 | border-left: 1px solid #ccc; 110 | position: absolute; 111 | } 112 | 113 | /* DEFAULT THEME */ 114 | 115 | .cm-s-default .cm-header {color: blue;} 116 | .cm-s-default .cm-quote {color: #090;} 117 | .cm-negative {color: #d44;} 118 | .cm-positive {color: #292;} 119 | .cm-header, .cm-strong {font-weight: bold;} 120 | .cm-em {font-style: italic;} 121 | .cm-link {text-decoration: underline;} 122 | .cm-strikethrough {text-decoration: line-through;} 123 | 124 | .cm-s-default .cm-keyword {color: #708;} 125 | .cm-s-default .cm-atom {color: #219;} 126 | .cm-s-default .cm-number {color: #164;} 127 | .cm-s-default .cm-def {color: #00f;} 128 | .cm-s-default .cm-variable, 129 | .cm-s-default .cm-punctuation, 130 | .cm-s-default .cm-property, 131 | .cm-s-default .cm-operator {} 132 | .cm-s-default .cm-variable-2 {color: #05a;} 133 | .cm-s-default .cm-variable-3 {color: #085;} 134 | .cm-s-default .cm-comment {color: #a50;} 135 | .cm-s-default .cm-string {color: #a11;} 136 | .cm-s-default .cm-string-2 {color: #f50;} 137 | .cm-s-default .cm-meta {color: #555;} 138 | .cm-s-default .cm-qualifier {color: #555;} 139 | .cm-s-default .cm-builtin {color: #30a;} 140 | .cm-s-default .cm-bracket {color: #997;} 141 | .cm-s-default .cm-tag {color: #170;} 142 | .cm-s-default .cm-attribute {color: #00c;} 143 | .cm-s-default .cm-hr {color: #999;} 144 | .cm-s-default .cm-link {color: #00c;} 145 | 146 | .cm-s-default .cm-error {color: #f00;} 147 | .cm-invalidchar {color: #f00;} 148 | 149 | .CodeMirror-composing { border-bottom: 2px solid; } 150 | 151 | /* Default styles for common addons */ 152 | 153 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 154 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 155 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 156 | .CodeMirror-activeline-background {background: #e8f2ff;} 157 | 158 | /* STOP */ 159 | 160 | /* The rest of this file contains styles related to the mechanics of 161 | the editor. You probably shouldn't touch them. */ 162 | 163 | .CodeMirror { 164 | position: relative; 165 | overflow: hidden; 166 | background: white; 167 | } 168 | 169 | .CodeMirror-scroll { 170 | overflow: scroll !important; /* Things will break if this is overridden */ 171 | /* 30px is the magic margin used to hide the element's real scrollbars */ 172 | /* See overflow: hidden in .CodeMirror */ 173 | margin-bottom: -30px; margin-right: -30px; 174 | padding-bottom: 30px; 175 | height: 100%; 176 | outline: none; /* Prevent dragging from highlighting the element */ 177 | position: relative; 178 | } 179 | .CodeMirror-sizer { 180 | position: relative; 181 | border-right: 30px solid transparent; 182 | } 183 | 184 | /* The fake, visible scrollbars. Used to force redraw during scrolling 185 | before actuall scrolling happens, thus preventing shaking and 186 | flickering artifacts. */ 187 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 188 | position: absolute; 189 | z-index: 6; 190 | display: none; 191 | } 192 | .CodeMirror-vscrollbar { 193 | right: 0; top: 0; 194 | overflow-x: hidden; 195 | overflow-y: scroll; 196 | } 197 | .CodeMirror-hscrollbar { 198 | bottom: 0; left: 0; 199 | overflow-y: hidden; 200 | overflow-x: scroll; 201 | } 202 | .CodeMirror-scrollbar-filler { 203 | right: 0; bottom: 0; 204 | } 205 | .CodeMirror-gutter-filler { 206 | left: 0; bottom: 0; 207 | } 208 | 209 | .CodeMirror-gutters { 210 | position: absolute; left: 0; top: 0; 211 | z-index: 3; 212 | } 213 | .CodeMirror-gutter { 214 | white-space: normal; 215 | height: 100%; 216 | display: inline-block; 217 | margin-bottom: -30px; 218 | /* Hack to make IE7 behave */ 219 | *zoom:1; 220 | *display:inline; 221 | } 222 | .CodeMirror-gutter-wrapper { 223 | position: absolute; 224 | z-index: 4; 225 | background: none !important; 226 | border: none !important; 227 | } 228 | .CodeMirror-gutter-background { 229 | position: absolute; 230 | top: 0; bottom: 0; 231 | z-index: 4; 232 | } 233 | .CodeMirror-gutter-elt { 234 | position: absolute; 235 | cursor: default; 236 | z-index: 4; 237 | } 238 | .CodeMirror-gutter-wrapper { 239 | -webkit-user-select: none; 240 | -moz-user-select: none; 241 | user-select: none; 242 | } 243 | 244 | .CodeMirror-lines { 245 | cursor: text; 246 | min-height: 1px; /* prevents collapsing before first draw */ 247 | } 248 | .CodeMirror pre { 249 | /* Reset some styles that the rest of the page might have set */ 250 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 251 | border-width: 0; 252 | background: transparent; 253 | font-family: inherit; 254 | font-size: inherit; 255 | margin: 0; 256 | white-space: pre; 257 | word-wrap: normal; 258 | line-height: inherit; 259 | color: inherit; 260 | z-index: 2; 261 | position: relative; 262 | overflow: visible; 263 | -webkit-tap-highlight-color: transparent; 264 | } 265 | .CodeMirror-wrap pre { 266 | word-wrap: break-word; 267 | white-space: pre-wrap; 268 | word-break: normal; 269 | } 270 | 271 | .CodeMirror-linebackground { 272 | position: absolute; 273 | left: 0; right: 0; top: 0; bottom: 0; 274 | z-index: 0; 275 | } 276 | 277 | .CodeMirror-linewidget { 278 | position: relative; 279 | z-index: 2; 280 | overflow: auto; 281 | } 282 | 283 | .CodeMirror-widget {} 284 | 285 | .CodeMirror-code { 286 | outline: none; 287 | } 288 | 289 | /* Force content-box sizing for the elements where we expect it */ 290 | .CodeMirror-scroll, 291 | .CodeMirror-sizer, 292 | .CodeMirror-gutter, 293 | .CodeMirror-gutters, 294 | .CodeMirror-linenumber { 295 | -moz-box-sizing: content-box; 296 | box-sizing: content-box; 297 | } 298 | 299 | .CodeMirror-measure { 300 | position: absolute; 301 | width: 100%; 302 | height: 0; 303 | overflow: hidden; 304 | visibility: hidden; 305 | } 306 | 307 | .CodeMirror-cursor { position: absolute; } 308 | .CodeMirror-measure pre { position: static; } 309 | 310 | div.CodeMirror-cursors { 311 | visibility: hidden; 312 | position: relative; 313 | z-index: 3; 314 | } 315 | div.CodeMirror-dragcursors { 316 | visibility: visible; 317 | } 318 | 319 | .CodeMirror-focused div.CodeMirror-cursors { 320 | visibility: visible; 321 | } 322 | 323 | .CodeMirror-selected { background: #d9d9d9; } 324 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 325 | .CodeMirror-crosshair { cursor: crosshair; } 326 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 327 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 328 | 329 | .cm-searching { 330 | background: #ffa; 331 | background: rgba(255, 255, 0, .4); 332 | } 333 | 334 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 335 | .CodeMirror span { *vertical-align: text-bottom; } 336 | 337 | /* Used to force a border model for a node */ 338 | .cm-force-border { padding-right: .1px; } 339 | 340 | @media print { 341 | /* Hide the cursor when printing */ 342 | .CodeMirror div.CodeMirror-cursors { 343 | visibility: hidden; 344 | } 345 | } 346 | 347 | /* See issue #2901 */ 348 | .cm-tab-wrap-hack:after { content: ''; } 349 | 350 | /* Help users use markselection to safely style text background */ 351 | span.CodeMirror-selectedtext { background: none; } -------------------------------------------------------------------------------- /resources/code-mirror/colorize.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("./runmode")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "./runmode"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; 15 | 16 | function textContent(node, out) { 17 | if (node.nodeType == 3) return out.push(node.nodeValue); 18 | for (var ch = node.firstChild; ch; ch = ch.nextSibling) { 19 | textContent(ch, out); 20 | if (isBlock.test(node.nodeType)) out.push("\n"); 21 | } 22 | } 23 | 24 | CodeMirror.colorize = function(collection, defaultMode) { 25 | if (!collection) collection = document.body.getElementsByTagName("pre"); 26 | 27 | for (var i = 0; i < collection.length; ++i) { 28 | var node = collection[i]; 29 | var mode = node.getAttribute("data-lang") || defaultMode; 30 | if (!mode) continue; 31 | 32 | var text = []; 33 | textContent(node, text); 34 | node.innerHTML = ""; 35 | CodeMirror.runMode(text.join(""), mode, node); 36 | 37 | node.className += " cm-s-default"; 38 | } 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /resources/code-mirror/runmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.runMode = function(string, modespec, callback, options) { 15 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); 16 | var ie = /MSIE \d/.test(navigator.userAgent); 17 | var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); 18 | 19 | if (callback.appendChild) { 20 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; 21 | var node = callback, col = 0; 22 | node.innerHTML = ""; 23 | callback = function(text, style) { 24 | if (text == "\n") { 25 | // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. 26 | // Emitting a carriage return makes everything ok. 27 | node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text)); 28 | col = 0; 29 | return; 30 | } 31 | var content = ""; 32 | // replace tabs 33 | for (var pos = 0;;) { 34 | var idx = text.indexOf("\t", pos); 35 | if (idx == -1) { 36 | content += text.slice(pos); 37 | col += text.length - pos; 38 | break; 39 | } else { 40 | col += idx - pos; 41 | content += text.slice(pos, idx); 42 | var size = tabSize - col % tabSize; 43 | col += size; 44 | for (var i = 0; i < size; ++i) content += " "; 45 | pos = idx + 1; 46 | } 47 | } 48 | 49 | if (style) { 50 | var sp = node.appendChild(document.createElement("span")); 51 | sp.className = "cm-" + style.replace(/ +/g, " cm-"); 52 | sp.appendChild(document.createTextNode(content)); 53 | } else { 54 | node.appendChild(document.createTextNode(content)); 55 | } 56 | }; 57 | } 58 | 59 | var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); 60 | for (var i = 0, e = lines.length; i < e; ++i) { 61 | if (i) callback("\n"); 62 | var stream = new CodeMirror.StringStream(lines[i]); 63 | if (!stream.string && mode.blankLine) mode.blankLine(state); 64 | while (!stream.eol()) { 65 | var style = mode.token(stream, state); 66 | callback(stream.current(), style, i, stream.start, state); 67 | stream.start = stream.pos; 68 | } 69 | } 70 | }; 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /resources/code-mirror/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 36 | 39 | 40 | 41 | 42 | ${code} 43 | 48 | 49 | -------------------------------------------------------------------------------- /resources/icons/download_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StepicOrg/intellij-plugin/844e500b7e4793b8db110a5cbd91b53a1464ea71/resources/icons/download_blue.png -------------------------------------------------------------------------------- /resources/icons/stepik_logo_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StepicOrg/intellij-plugin/844e500b7e4793b8db110a5cbd91b53a1464ea71/resources/icons/stepik_logo_green.png -------------------------------------------------------------------------------- /resources/icons/stepik_logotype_13x13-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StepicOrg/intellij-plugin/844e500b7e4793b8db110a5cbd91b53a1464ea71/resources/icons/stepik_logotype_13x13-2.png -------------------------------------------------------------------------------- /resources/style/javaFXBrowserDarcula.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar-corner { 2 | background-color: #3c3f41; 3 | } 4 | 5 | body { 6 | background-color: #3c3f41; 7 | color: #bababa 8 | } 9 | 10 | p img { 11 | background-color: #bababa; 12 | } 13 | 14 | textarea { 15 | background-color: #3c3f41; 16 | color: #bababa 17 | } 18 | 19 | a { 20 | color: #589df6 21 | } 22 | 23 | tr { 24 | text-align: left 25 | } -------------------------------------------------------------------------------- /resources/style/javaFXBrowserDarculaScrollBar.css: -------------------------------------------------------------------------------- 1 | 2 | .scroll-bar{ 3 | -fx-background-color: #3c3f41; 4 | -fx-background-radius: 2em; 5 | } 6 | .scroll-bar:horizontal .track, 7 | .scroll-bar:vertical .track { 8 | -fx-background-color: #3c3f41; 9 | -fx-border-color:transparent; 10 | -fx-background-radius: 2em; 11 | } 12 | .scroll-bar:vertical .track-background, 13 | .scroll-bar:horizontal .track-background { 14 | -fx-background-color: #3c3f41; 15 | } 16 | .scroll-bar:horizontal .thumb { 17 | -fx-background-color: #bbbbbb; 18 | -fx-background-insets: 4 0 4 0; 19 | -fx-background-radius: 2em; 20 | -fx-opacity: 0.2; 21 | } 22 | .scroll-bar:vertical .thumb { 23 | -fx-background-color: #bbbbbb; 24 | -fx-background-insets: 0 4 0 4; 25 | -fx-background-radius: 2em; 26 | -fx-opacity: 0.2; 27 | } 28 | 29 | 30 | .scroll-bar:vertical:corner-present { 31 | -fx-shape: " "; 32 | -fx-width: 0; 33 | } 34 | 35 | .scroll-bar:horizontal:corner-present { 36 | -fx-shape: " "; 37 | -fx-width: 0; 38 | } 39 | 40 | .scroll-bar-corner { 41 | -fx-background-color: #3c3f41; 42 | } 43 | .scroll-bar:horizontal .increment-arrow { 44 | -fx-shape: " " 45 | } 46 | .scroll-bar:vertical .increment-arrow { 47 | -fx-shape: " " 48 | } 49 | .scroll-bar:horizontal .decrement-arrow { 50 | -fx-shape: " " 51 | } 52 | .scroll-bar:vertical .decrement-arrow { 53 | -fx-shape: " " 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /resources/style/javaFXBrowserScrollBar.css: -------------------------------------------------------------------------------- 1 | 2 | .scroll-bar{ 3 | -fx-background-color: #ffffff; 4 | -fx-background-radius: 2em; 5 | } 6 | .scroll-bar:horizontal .track, 7 | .scroll-bar:vertical .track { 8 | -fx-background-color: #ffffff; 9 | -fx-border-color:transparent; 10 | -fx-background-radius: 2em; 11 | } 12 | .scroll-bar:vertical .track-background, 13 | .scroll-bar:horizontal .track-background { 14 | -fx-background-color: #ffffff; 15 | } 16 | .scroll-bar:horizontal .thumb { 17 | -fx-background-color: #dddddd; 18 | -fx-background-insets: 4 0 4 0; 19 | -fx-background-radius: 2em; 20 | } 21 | .scroll-bar:vertical .thumb { 22 | -fx-background-color: #dddddd; 23 | -fx-background-insets: 0 4 0 4; 24 | -fx-background-radius: 2em; 25 | } 26 | 27 | .scroll-bar:vertical:corner-present { 28 | -fx-shape: " "; 29 | -fx-width: 0; 30 | } 31 | 32 | .scroll-bar:horizontal:corner-present { 33 | -fx-shape: " "; 34 | -fx-width: 0; 35 | } 36 | 37 | .scroll-bar-corner { 38 | -fx-background-color: #ffffff; 39 | } 40 | .scroll-bar:horizontal .increment-arrow { 41 | -fx-shape: " " 42 | } 43 | .scroll-bar:vertical .increment-arrow { 44 | -fx-shape: " " 45 | } 46 | .scroll-bar:horizontal .decrement-arrow { 47 | -fx-shape: " " 48 | } 49 | .scroll-bar:vertical .decrement-arrow { 50 | -fx-shape: " " 51 | } -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/Help.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.ui.Messages; 5 | 6 | public class Help extends MainMenuAction { 7 | 8 | private static String text = "Stepik plugin for code challenges on Java.\n" + 9 | "\n" + 10 | "To work with plugin you must to create a project of Stepik type.\n" + 11 | "You need to enter login and password your Stepik account.\n" + 12 | "And then input course URL or course number.\n" + 13 | "After twenty seconds course will be built.\n" + 14 | "\n" + 15 | "To send task click the left mouse button on file. In the shortcut menu choose \"Submit Solution\".\n" + 16 | "In the same way you can to download last submissions and get the status of step.\n" + 17 | "\n" + 18 | "\"Who am I\" -- returns your user_name.\n" + 19 | "\"Update course\" -- download new lessons.\n" + 20 | "\n\n--------------------------------------\n\n" + 21 | "Stepik плагин для решения задач на программирование на языке Java.\n" + 22 | "\n" + 23 | "Для работы с плагином необходимо создать проект типа Stepik.\n" + 24 | "Ввести логин и пароль от аккаунта.\n" + 25 | "Затем ввести ссылку на курс или его номер.\n" + 26 | "Через двадцать секунд проект будет построен.\n" + 27 | "\n" + 28 | "Для отправки задачи кликните левой кнопкой мыши на файле. В контекстном меню выберете \"Submit Solution\".\n" + 29 | "Тем же способом вы можете загрузить последний отправленный вариант или узнать статус задачи.\n" + 30 | "\n" + 31 | "\"Who am I\" -- возвращает user_name.\n" + 32 | "\"Update course\" -- загружает новые уроки.\n"; 33 | 34 | @Override 35 | public void actionPerformed(AnActionEvent e) { 36 | Messages.showMessageDialog(e.getProject(), text, "Information", Messages.getInformationIcon()); 37 | } 38 | 39 | @Override 40 | public void update(AnActionEvent e) { 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/MainMenuAction.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import org.stepik.plugin.storages.ActionVisibleProperties; 6 | 7 | public abstract class MainMenuAction extends AnAction { 8 | @Override 9 | public void update(AnActionEvent e) { 10 | ActionVisibleProperties properties = ActionVisibleProperties.getInstance(e.getProject()); 11 | e.getPresentation().setEnabled(properties.isEnabled()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/NegTranslator.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.ui.Messages; 5 | import org.stepik.plugin.storages.CourseDefinitionStorage; 6 | 7 | /** 8 | * Created by Petr on 03.06.2016. 9 | */ 10 | public class NegTranslator extends MainMenuAction { 11 | 12 | @Override 13 | public void actionPerformed(AnActionEvent e) { 14 | CourseDefinitionStorage ws = CourseDefinitionStorage.getInstance(e.getProject()); 15 | 16 | ws.setTranslate(!ws.isTranslate()); 17 | Messages.showMessageDialog(e.getProject(), Boolean.toString(ws.isTranslate()), "Information", Messages.getInformationIcon()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/PrintMapInfo.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.Messages; 6 | import org.stepik.plugin.modules.StepInfo; 7 | import org.stepik.plugin.storages.CourseDefinitionStorage; 8 | 9 | import java.util.Map; 10 | 11 | public class PrintMapInfo extends MainMenuAction { 12 | 13 | @Override 14 | public void actionPerformed(AnActionEvent e) { 15 | Project project = e.getProject(); 16 | 17 | CourseDefinitionStorage ws = CourseDefinitionStorage.getInstance(e.getProject()); 18 | final String[] text = {""}; 19 | 20 | Map map = ws.getMapPathInfo(); 21 | if (map == null) { 22 | Messages.showMessageDialog(project, "path--step is null", "Information", Messages.getInformationIcon()); 23 | } else { 24 | map.keySet().forEach((x) -> text[0] += x + "\n"); 25 | Messages.showMessageDialog(project, text[0], "Information", Messages.getInformationIcon()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/RefreshMap.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.project.Project; 5 | import org.stepik.plugin.utils.Utils; 6 | 7 | public class RefreshMap extends MainMenuAction { 8 | 9 | @Override 10 | public void actionPerformed(AnActionEvent e) { 11 | Project project = e.getProject(); 12 | Utils.refreshFiles(project); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/RefreshToken.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import org.stepik.plugin.stepikConnector.StepikConnector; 5 | 6 | public class RefreshToken extends MainMenuAction { 7 | 8 | @Override 9 | public void actionPerformed(AnActionEvent e) { 10 | StepikConnector.initToken(e.getProject()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/SignIn.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.Messages; 7 | import org.stepik.plugin.stepikConnector.StepikConnector; 8 | 9 | public class SignIn extends MainMenuAction { 10 | 11 | @Override 12 | public void actionPerformed(AnActionEvent e) { 13 | Project project = e.getData(PlatformDataKeys.PROJECT); 14 | 15 | String login = 16 | Messages.showInputDialog(project, "Please, input your E-mail", "Sing in", Messages.getQuestionIcon()); 17 | String password = 18 | Messages.showPasswordDialog(project, "Please, input your Password", "Sing in", Messages.getQuestionIcon()); 19 | 20 | StepikConnector.setLoginAndPassword(login, password, project); 21 | StepikConnector.initToken(e.getProject()); 22 | String name = StepikConnector.getUserName(project); 23 | 24 | Messages.showMessageDialog(project, "Hello, " + name + "!\n I am glad to see you.", "Information", Messages.getInformationIcon()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/SomeAction.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.CommonDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.PsiManager; 9 | import com.intellij.refactoring.RefactoringFactory; 10 | import com.intellij.refactoring.RenameRefactoring; 11 | 12 | public class SomeAction extends MainMenuAction { 13 | 14 | @Override 15 | public void actionPerformed(AnActionEvent e) { 16 | Project project = e.getProject(); 17 | VirtualFile vf = e.getData(CommonDataKeys.VIRTUAL_FILE); 18 | if (vf == null) return; 19 | 20 | PsiFile psi = PsiManager.getInstance(project).findFile(vf); 21 | RenameRefactoring rr = RefactoringFactory.getInstance(project).createRename(psi, "step"); 22 | 23 | psi.setName("Abra"); 24 | 25 | 26 | } 27 | 28 | 29 | public void update(AnActionEvent anActionEvent) { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/UpdateCourse.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.Messages; 7 | import com.intellij.openapi.vfs.LocalFileSystem; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import org.stepik.plugin.modules.Course; 10 | import org.stepik.plugin.modules.StepInfo; 11 | import org.stepik.plugin.projectWizard.StepikModuleBuilder; 12 | import org.stepik.plugin.stepikConnector.StepikConnector; 13 | import org.stepik.plugin.storages.ActionVisibleProperties; 14 | import org.stepik.plugin.storages.CourseDefinitionStorage; 15 | import org.stepik.plugin.storages.StepikApplicationStorage; 16 | import org.stepik.plugin.utils.Utils; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.nio.charset.Charset; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import java.nio.file.Paths; 24 | import java.util.HashMap; 25 | import java.util.HashSet; 26 | import java.util.Map; 27 | import java.util.Set; 28 | 29 | public class UpdateCourse extends MainMenuAction { 30 | private static final Logger LOG = Logger.getInstance(UpdateCourse.class); 31 | private static String success = "Course is successfully updated."; 32 | private static String error = "Course was not updated."; 33 | private static String old = "No new lessons."; 34 | private static String nnew = "New lessons are created."; 35 | private static String course_is_broken = "Your course_no is broke.\nPlease insert correct course_no or course_link."; 36 | private static String login_is_broken = "Your login is broke.\nPlease insert your e-mail on Stepik account."; 37 | private static String password_is_broken = "Your password is broke.\nPlease insert your password on Stepik account."; 38 | 39 | @Override 40 | public void actionPerformed(AnActionEvent e) { 41 | final Project project = e.getProject(); 42 | 43 | final VirtualFile root = project.getBaseDir(); 44 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 45 | String courseID = projectService.getCourseID(); 46 | String login = StepikConnector.getLogin(project); 47 | projectService.setProjectName(project.getName()); 48 | 49 | 50 | ActionVisibleProperties prop = ActionVisibleProperties.getInstance(project); 51 | if (isCourseBroke(courseID, login) || !prop.isVisible()) { 52 | if (Messages.showYesNoDialog(project, "Your project of course is broke.\nDo you want to repair it?", "Repair", Messages.getQuestionIcon()) == 0) { 53 | if (courseID == null || courseID.isEmpty()) { 54 | repairCourseID(project); 55 | } 56 | 57 | if (login == null || login.isEmpty()) { 58 | repairLoginAndPassword(project); 59 | } 60 | 61 | prop.setEnabled(true); 62 | prop.setVisible(true); 63 | } else { 64 | return; 65 | } 66 | } 67 | StepikConnector.initToken(project); 68 | 69 | 70 | Map map; 71 | Set newFiles = new HashSet<>(); 72 | Course course = StepikConnector.getCourse(courseID, project); 73 | 74 | Utils.refreshFiles(project); 75 | map = new HashMap<>(projectService.getMapPathInfo()); 76 | course.build(root.getPath(), project); 77 | 78 | projectService.mapPathInfo.entrySet().forEach(x -> { 79 | if (!map.containsKey(x.getKey())) { 80 | String path = x.getKey(); 81 | StepInfo stepInfo = x.getValue(); 82 | 83 | File file = new File(path); 84 | if (!file.exists()) { 85 | file.getParentFile().mkdirs(); 86 | newFiles.add(path); 87 | try { 88 | Path path1 = Paths.get(path); 89 | Files.write(path1, StepikModuleBuilder.getText(stepInfo.getFilename(), stepInfo.getPackageName()), Charset.forName("UTF-8")); 90 | } catch (IOException ex) { 91 | LOG.error("Create file error\n" + ex.getMessage()); 92 | } 93 | } 94 | } 95 | }); 96 | 97 | LocalFileSystem.getInstance().refresh(true); 98 | 99 | StringBuilder sb = new StringBuilder(); 100 | sb.append(success + "\n"); 101 | if (newFiles.isEmpty()) { 102 | sb.append(old); 103 | } else { 104 | sb.append(nnew); 105 | } 106 | Messages.showMessageDialog(project, sb.toString(), "Information", Messages.getInformationIcon()); 107 | 108 | } 109 | 110 | private boolean isCourseBroke(String courseID, String login) { 111 | return courseID == null || courseID.isEmpty() || login == null || login.isEmpty(); 112 | } 113 | 114 | private void repairCourseID(Project project) { 115 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 116 | String courseID = Messages.showInputDialog(project, course_is_broken, "Repair", Messages.getQuestionIcon()); 117 | courseID = Utils.parseUrl(courseID); 118 | boolean translate = Messages.showYesNoDialog(project, "Do you want to translate package names?", "Translator", Messages.getQuestionIcon()) == 0; 119 | projectService.setCourseID(courseID); 120 | projectService.setTranslate(translate); 121 | ActionVisibleProperties prop = ActionVisibleProperties.getInstance(project); 122 | prop.setEnabled(true); 123 | prop.setVisible(true); 124 | } 125 | 126 | private void repairLoginAndPassword(Project project) { 127 | String login = Messages.showInputDialog(project, login_is_broken, "Repair", Messages.getQuestionIcon(), StepikApplicationStorage.getInstance().getLogin(), null); 128 | 129 | if (StepikConnector.isPasswordSet(project)) { 130 | String password = Messages.showInputDialog(project, password_is_broken, "Repair", Messages.getQuestionIcon()); 131 | StepikConnector.setLoginAndPassword(login, password, project); 132 | } else { 133 | StepikConnector.setLogin(login, project); 134 | } 135 | } 136 | 137 | @Override 138 | public void update(AnActionEvent e) { 139 | // super.update(e); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/mainMenu/WhoAmI.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.mainMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.ui.Messages; 9 | import org.stepik.plugin.stepikConnector.StepikConnector; 10 | 11 | public class WhoAmI extends MainMenuAction { 12 | private static final Logger LOG = Logger.getInstance(AnAction.class); 13 | 14 | @Override 15 | public void actionPerformed(AnActionEvent e) { 16 | 17 | Project project = e.getData(PlatformDataKeys.PROJECT); 18 | String login = StepikConnector.getLogin(project); 19 | String name = null; 20 | name = StepikConnector.getUserName(project); 21 | Messages.showMessageDialog(project, "Hello, " + name + "!\n I am glad to see you.", "Information", Messages.getInformationIcon()); 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/popupMenu/DownloadLastSubmission.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.popupMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.CommonDataKeys; 5 | import com.intellij.openapi.application.ApplicationManager; 6 | import com.intellij.openapi.command.CommandProcessor; 7 | import com.intellij.openapi.editor.Document; 8 | import com.intellij.openapi.fileEditor.FileDocumentManager; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import org.stepik.plugin.modules.Submission; 12 | import org.stepik.plugin.stepikConnector.StepikConnector; 13 | import org.stepik.plugin.storages.CourseDefinitionStorage; 14 | import org.stepik.plugin.utils.NotificationTemplates; 15 | import org.stepik.plugin.utils.NotificationUtils; 16 | 17 | import java.util.HashSet; 18 | import java.util.List; 19 | import java.util.Set; 20 | 21 | public class DownloadLastSubmission extends PopupMenuAction { 22 | 23 | @Override 24 | public void actionPerformed(AnActionEvent e) { 25 | Project project = e.getProject(); 26 | VirtualFile vf = e.getData(CommonDataKeys.VIRTUAL_FILE); 27 | if (vf == null) return; 28 | 29 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 30 | String stepName = vf.getName().split("\\.")[0]; 31 | 32 | List submissions = 33 | StepikConnector.getSubmissions(projectService.getStepID(vf.getPath()), project); 34 | 35 | if (submissions.isEmpty()) { 36 | NotificationUtils.showNotification(NotificationTemplates.DOWNLOAD_WARNING, project); 37 | } else { 38 | String code = submissions.get(submissions.size() - 1).getCode(); 39 | String pack = CourseDefinitionStorage.getInstance(project).getPackageName(vf.getPath()); 40 | Document doc = FileDocumentManager.getInstance().getDocument(vf); 41 | String code2 = renameFromMainToStep(code, stepName, pack); 42 | CommandProcessor.getInstance().executeCommand(project, 43 | () -> ApplicationManager.getApplication().runWriteAction( 44 | () -> { 45 | doc.setText(code2); 46 | }), "Download last submission", "Download last submission"); 47 | } 48 | } 49 | 50 | private String renameFromMainToStep(String code, String stepName, String ppackage) { 51 | String[] lines = code.split("\n"); 52 | 53 | Set skipLine = new HashSet<>(); 54 | for (int i = 0; i < lines.length; i++) { 55 | if (lines[i].contains("// Sent from IntelliJ IDEA")) skipLine.add(i); 56 | if (lines[i].contains("package")) skipLine.add(i); 57 | if (lines[i].contains("class Main")) { 58 | lines[i] = "class " + stepName + " {"; 59 | break; 60 | } 61 | } 62 | 63 | StringBuilder sb = new StringBuilder(); 64 | sb.append("package " + ppackage + ";\n\n"); 65 | for (int i = 0; i < lines.length; i++) { 66 | if (skipLine.contains(i)) continue; 67 | sb.append(lines[i] + "\n"); 68 | } 69 | return sb.toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/popupMenu/GetIdStep.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.popupMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.CommonDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.Messages; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import org.stepik.plugin.storages.CourseDefinitionStorage; 9 | 10 | /** 11 | * Created by Petr on 21.05.2016. 12 | */ 13 | public class GetIdStep extends PopupMenuAction { 14 | 15 | @Override 16 | public void actionPerformed(AnActionEvent e) { 17 | Project project = e.getProject(); 18 | VirtualFile vf = e.getData(CommonDataKeys.VIRTUAL_FILE); 19 | if (vf == null) return; 20 | 21 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 22 | String stepId = projectService.getStepID(vf.getPath()); 23 | Messages.showMessageDialog(project, stepId, "Information", Messages.getInformationIcon()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/popupMenu/GetStepStatus.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.popupMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.CommonDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.Messages; 7 | import com.intellij.openapi.util.Pair; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import org.stepik.plugin.stepikConnector.StepikConnector; 10 | import org.stepik.plugin.storages.CourseDefinitionStorage; 11 | 12 | public class GetStepStatus extends PopupMenuAction { 13 | 14 | @Override 15 | public void actionPerformed(AnActionEvent e) { 16 | Project project = e.getProject(); 17 | VirtualFile vf = e.getData(CommonDataKeys.VIRTUAL_FILE); 18 | if (vf == null) return; 19 | 20 | String path = vf.getPath(); 21 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 22 | String stepId = projectService.getStepID(path); 23 | 24 | String ans; 25 | 26 | 27 | if (wasItSolved(stepId, project)) { 28 | ans = "Step was solved\n"; 29 | } else { 30 | ans = "Step wasn't solved\n"; 31 | } 32 | 33 | ans += "last submission from IDEA is "; 34 | String subID = projectService.getSubmissionID(path); 35 | if (subID.isEmpty()) { 36 | ans += "unknown"; 37 | } else { 38 | ans += StepikConnector.getStatus(subID, project).get(0).getStatus(); 39 | } 40 | 41 | 42 | Messages.showMessageDialog(project, ans, "Information", Messages.getInformationIcon()); 43 | } 44 | 45 | private boolean wasItSolved(String stepID, Project project) { 46 | int size = StepikConnector.getStepStatus(stepID, Pair.pair("status", "correct"), project).size(); 47 | return size > 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/popupMenu/PopupMenuAction.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.popupMenu; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.CommonDataKeys; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import org.stepik.plugin.storages.ActionVisibleProperties; 8 | 9 | public abstract class PopupMenuAction extends AnAction { 10 | 11 | @Override 12 | public void update(AnActionEvent anActionEvent) { 13 | VirtualFile vf = anActionEvent.getData(CommonDataKeys.VIRTUAL_FILE); 14 | if (vf == null) return; 15 | 16 | ActionVisibleProperties properties = ActionVisibleProperties.getInstance(anActionEvent.getProject()); 17 | anActionEvent.getPresentation().setVisible(properties.isVisible() && !vf.isDirectory()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/actions/popupMenu/SendFile.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.actions.popupMenu; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationType; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.CommonDataKeys; 7 | import com.intellij.openapi.application.Application; 8 | import com.intellij.openapi.application.ApplicationManager; 9 | import com.intellij.openapi.editor.Document; 10 | import com.intellij.openapi.fileEditor.FileDocumentManager; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.openapi.ui.Messages; 13 | import com.intellij.openapi.vfs.VirtualFile; 14 | import org.stepik.plugin.modules.Submission; 15 | import org.stepik.plugin.stepikConnector.StepikConnector; 16 | import org.stepik.plugin.storages.CourseDefinitionStorage; 17 | import org.stepik.plugin.utils.MyLogger; 18 | import org.stepik.plugin.utils.NotificationUtils; 19 | 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | public class SendFile extends PopupMenuAction { 25 | private static String success = "the Step successfully sent"; 26 | private static String error = "the Step is not sent"; 27 | 28 | @Override 29 | public void actionPerformed(AnActionEvent event) { 30 | Project project = event.getProject(); 31 | VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE); 32 | if (vf == null) return; 33 | 34 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 35 | String stepId = projectService.getStepID(vf.getPath()); 36 | 37 | String text = renameMainClass(vf); 38 | String path = vf.getPath(); 39 | 40 | String attemptId = StepikConnector.getAttemptId(stepId, project); 41 | String submissionId = StepikConnector.sendFile(text, attemptId, project); 42 | 43 | MyLogger.getInstance().getLOG().debug("attId = " + attemptId); 44 | MyLogger.getInstance().getLOG().debug("subId = " + submissionId); 45 | 46 | projectService.setAttemptID(path, attemptId); 47 | projectService.setSubmissionID(path, submissionId); 48 | Messages.showMessageDialog(project, success, "Information", Messages.getInformationIcon()); 49 | 50 | String filename = projectService.getFilename(path); 51 | final Application application = ApplicationManager.getApplication(); 52 | final String finalSubmissionId = submissionId; 53 | application.executeOnPooledThread( 54 | () -> { 55 | String ans = "evaluation"; 56 | final int TIMER = 2; 57 | int count = 0; 58 | Notification notification = null; 59 | List list = null; 60 | String b = ""; 61 | while (ans.equals("evaluation") && count < 100) { 62 | try { 63 | Thread.sleep(TIMER * 1000); //1000 milliseconds is one second. 64 | list = StepikConnector.getStatus(finalSubmissionId, project); 65 | if (list != null) { 66 | ans = list.get(0).getStatus(); 67 | b = list.get(0).getHint(); 68 | } 69 | count += TIMER; 70 | } catch (InterruptedException | NullPointerException e) { 71 | notification = new Notification("Step.sending", "Error", "Get Status error", NotificationType.ERROR); 72 | NotificationUtils.showNotification(notification, project); 73 | return; 74 | } 75 | } 76 | 77 | NotificationType notificationType; 78 | 79 | if (ans.equals("correct")) { 80 | notificationType = NotificationType.INFORMATION; 81 | b = "Success!"; 82 | } else { 83 | notificationType = NotificationType.WARNING; 84 | b = b.split("\\.")[0]; 85 | } 86 | notification = new Notification("Step.sending", filename + " is " + ans, b, notificationType); 87 | NotificationUtils.showNotification(notification, project); 88 | } 89 | ); 90 | } 91 | 92 | private String renameMainClass(VirtualFile vf) { 93 | Document doc = FileDocumentManager.getInstance().getDocument(vf); 94 | String[] lines = doc.getText().split("\n"); 95 | 96 | Set skipLine = new HashSet<>(); 97 | for (int i = 0; i < lines.length; i++) { 98 | if (lines[i].contains("// Sent from IntelliJ IDEA")) skipLine.add(i); 99 | if (lines[i].contains("package")) skipLine.add(i); 100 | if (lines[i].contains("class Step")) { 101 | lines[i] = "class Main {"; 102 | break; 103 | } 104 | } 105 | 106 | StringBuilder sb = new StringBuilder(); 107 | sb.append("// Sent from IntelliJ IDEA"); 108 | for (int i = 0; i < lines.length; i++) { 109 | if (skipLine.contains(i)) continue; 110 | sb.append(lines[i] + "\n"); 111 | } 112 | return sb.toString(); 113 | } 114 | 115 | @Override 116 | public void setDefaultIcon(boolean isDefaultIconSet) { 117 | super.setDefaultIcon(isDefaultIconSet); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/components/MyModuleComponent.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.components; 2 | 3 | import com.intellij.openapi.module.Module; 4 | import com.intellij.openapi.module.ModuleComponent; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class MyModuleComponent implements ModuleComponent { 8 | public MyModuleComponent(Module module) { 9 | } 10 | 11 | @Override 12 | public void initComponent() { 13 | // TODO: insert component initialization logic here 14 | } 15 | 16 | @Override 17 | public void disposeComponent() { 18 | // TODO: insert component disposal logic here 19 | } 20 | 21 | @Override 22 | @NotNull 23 | public String getComponentName() { 24 | return "MyModuleComponent"; 25 | } 26 | 27 | @Override 28 | public void projectOpened() { 29 | // called when project is opened 30 | } 31 | 32 | @Override 33 | public void projectClosed() { 34 | // called when project is being closed 35 | } 36 | 37 | @Override 38 | public void moduleAdded() { 39 | // Invoked when the module corresponding to this component instance has been completely 40 | // loaded and added to the project. 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/components/MyProjectComponent.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.components; 2 | 3 | import com.intellij.openapi.components.ProjectComponent; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.wm.ToolWindow; 6 | import com.intellij.openapi.wm.ToolWindowAnchor; 7 | import com.intellij.openapi.wm.ToolWindowManager; 8 | import javafx.application.Platform; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.stepik.plugin.stepikConnector.StepikConnector; 11 | import org.stepik.plugin.storages.CourseDefinitionStorage; 12 | import org.stepik.plugin.toolWindow.StudyToolWindowFactory; 13 | import org.stepik.plugin.toolWindow.StudyUtils; 14 | import org.stepik.plugin.utils.MyLogger; 15 | 16 | public class MyProjectComponent implements ProjectComponent { 17 | private Project project; 18 | 19 | public MyProjectComponent(Project project) { 20 | this.project = project; 21 | } 22 | 23 | @Override 24 | public void initComponent() { 25 | } 26 | 27 | @Override 28 | public void disposeComponent() { 29 | } 30 | 31 | @Override 32 | @NotNull 33 | public String getComponentName() { 34 | return "MyProjectComponent"; 35 | } 36 | 37 | @Override 38 | public void projectOpened() { 39 | MyLogger.getInstance().getLOG().debug("projectOpened"); 40 | 41 | if (StudyUtils.hasJavaFx()) { 42 | Platform.setImplicitExit(false); 43 | } 44 | 45 | if (project.getName().equals(CourseDefinitionStorage.getInstance(project).getProjectName())) { 46 | StepikConnector.initToken(project); 47 | registerStudyToolWindow(); 48 | } 49 | } 50 | 51 | private void registerStudyToolWindow() { 52 | MyLogger.getInstance().getLOG().debug("registerStudyToolWindow"); 53 | final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); 54 | registerToolWindows(toolWindowManager); 55 | final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); 56 | if (studyToolWindow != null) { 57 | StudyUtils.initToolWindows(project); 58 | } 59 | } 60 | 61 | private void registerToolWindows(@NotNull final ToolWindowManager toolWindowManager) { 62 | MyLogger.getInstance().getLOG().debug("registerToolWindows"); 63 | final ToolWindow toolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); 64 | if (toolWindow == null) { 65 | toolWindowManager.registerToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW, true, ToolWindowAnchor.RIGHT, project, true); 66 | } 67 | } 68 | 69 | @Override 70 | public void projectClosed() { 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/icons/PluginIcons.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.icons; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.util.IconLoader; 5 | 6 | import javax.swing.*; 7 | 8 | //public class PluginIcons { 9 | public interface PluginIcons { 10 | // public static final Icon STEPIK_LOGO = IconLoader.getIcon("/icons/stepik_logo_green.png"); 11 | Icon STEPIK_LOGO_MINI = IconLoader.getIcon("/icons/stepik_logotype_13x13-2.png"); 12 | Icon STEPIK_LOGO = IconLoader.getIcon("/icons/stepik_logo_green.png"); 13 | Icon DOWNLOAD = IconLoader.getIcon("/icons/download_blue.png"); 14 | // public static final Icon Question = AllIcons.General.TodoQuestion; 15 | Icon Question = AllIcons.General.TodoQuestion; 16 | 17 | } -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Attempt.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | public class Attempt { 4 | public int id; 5 | public String status; 6 | public String step; 7 | 8 | @Override 9 | public boolean equals(Object o) { 10 | if (this == o) return true; 11 | if (o == null || getClass() != o.getClass()) return false; 12 | 13 | Attempt attempt = (Attempt) o; 14 | 15 | if (id != attempt.id) return false; 16 | if (status != null ? !status.equals(attempt.status) : attempt.status != null) return false; 17 | return step != null ? step.equals(attempt.step) : attempt.step == null; 18 | 19 | } 20 | 21 | @Override 22 | public int hashCode() { 23 | int result = id; 24 | result = 31 * result + (status != null ? status.hashCode() : 0); 25 | result = 31 * result + (step != null ? step.hashCode() : 0); 26 | return result; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Course.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.intellij.openapi.project.Project; 5 | import org.stepik.plugin.stepikConnector.StepikConnector; 6 | import org.stepik.plugin.storages.CourseDefinitionStorage; 7 | import org.stepik.plugin.utils.Utils; 8 | import org.stepik.plugin.utils.YaTranslator; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class Course { 16 | public int id; 17 | public String title; 18 | public String summary; 19 | private String courseName; 20 | 21 | @SerializedName("sections") 22 | public List sectionsId; 23 | 24 | public transient Map sections = new HashMap<>(); 25 | 26 | public String getTitle() { 27 | return title; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "Course{" + 33 | "id=" + id + 34 | ", title='" + title + '\'' + 35 | ", sections=" + sections + 36 | '}'; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) return true; 42 | if (o == null || getClass() != o.getClass()) return false; 43 | Course that = (Course) o; 44 | return that.id == this.id; 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | int result = id; 50 | result = 31 * result + (title != null ? title.hashCode() : 0); 51 | result = 31 * result + (summary != null ? summary.hashCode() : 0); 52 | result = 31 * result + (courseName != null ? courseName.hashCode() : 0); 53 | result = 31 * result + (sectionsId != null ? sectionsId.hashCode() : 0); 54 | result = 31 * result + (sections != null ? sections.hashCode() : 0); 55 | return result; 56 | } 57 | 58 | public void build(String root, Project project) { 59 | int sectionNo = 0; 60 | List
sectionList = StepikConnector.getSections(Utils.getIdQuery(sectionsId), project); 61 | List sectionNames = new ArrayList<>(); 62 | sectionList.forEach(x -> sectionNames.add(x.title)); 63 | 64 | List newSectionNames = YaTranslator.translateNames(sectionNames, "section", project); 65 | 66 | for (Section section : sectionList) { 67 | section.setSectionName(newSectionNames.get(sectionNo)); 68 | sections.put(++sectionNo, section); 69 | section.build(sectionNo, root + "/" + getName(project), project); 70 | } 71 | } 72 | 73 | public String getName(Project project) { 74 | if (courseName == null) { 75 | CourseDefinitionStorage ws = CourseDefinitionStorage.getInstance(project); 76 | if (ws.isTranslate()) { 77 | courseName = YaTranslator.translateRuToEn(title); 78 | } else { 79 | courseName = "course"; 80 | } 81 | } 82 | return Utils.normalize(courseName); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/CourseInfo.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.intellij.openapi.util.text.StringUtil; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class CourseInfo { 11 | boolean is_public; 12 | public List sections; 13 | @SerializedName("title") 14 | private String myName; 15 | @SerializedName("summary") 16 | private String myDescription; 17 | @SerializedName("course_format") 18 | //course type in format "pycharm " 19 | private String myType = "pycharm Python"; 20 | 21 | public List instructors = new ArrayList(); 22 | 23 | List myAuthors = new ArrayList(); 24 | int id; 25 | 26 | public static CourseInfo INVALID_COURSE = new CourseInfo(); 27 | 28 | public String getName() { 29 | return myName; 30 | } 31 | 32 | @NotNull 33 | public List getAuthors() { 34 | return myAuthors; 35 | } 36 | 37 | public String getDescription() { 38 | return myDescription; 39 | } 40 | 41 | public String getType() { 42 | return myType; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return myName; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) return true; 53 | if (o == null || getClass() != o.getClass()) return false; 54 | CourseInfo that = (CourseInfo) o; 55 | if (that.getName() == null || that.getDescription() == null) return false; 56 | return that.getName().equals(myName) 57 | && that.getDescription().equals(myDescription); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | int result = myName != null ? myName.hashCode() : 0; 63 | result = 31 * result + (myDescription != null ? myDescription.hashCode() : 0); 64 | return result; 65 | } 66 | 67 | public static class Author { 68 | int id; 69 | String first_name = ""; 70 | String last_name = ""; 71 | 72 | public Author() { 73 | } 74 | 75 | public Author(String firstName, String lastName) { 76 | first_name = firstName; 77 | last_name = lastName; 78 | } 79 | 80 | public String getName() { 81 | return StringUtil.join(new String[]{first_name, last_name}, " "); 82 | } 83 | 84 | public int getId() { 85 | return id; 86 | } 87 | } 88 | 89 | public void setName(String name) { 90 | myName = name; 91 | } 92 | 93 | public void setAuthors(List authors) { 94 | myAuthors = authors; 95 | for (Author author : authors) { 96 | if (author.id > 0) { 97 | instructors.add(author.id); 98 | } 99 | } 100 | } 101 | 102 | public void addAuthor(Author author) { 103 | if (myAuthors == null) { 104 | myAuthors = new ArrayList(); 105 | } 106 | myAuthors.add(author); 107 | } 108 | 109 | public void setDescription(String description) { 110 | myDescription = description; 111 | } 112 | 113 | public void setType(String type) { 114 | myType = type; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Lesson.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.openapi.project.Project; 6 | import org.stepik.plugin.stepikConnector.StepikConnector; 7 | import org.stepik.plugin.storages.CourseDefinitionStorage; 8 | import org.stepik.plugin.utils.Utils; 9 | 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class Lesson { 15 | 16 | private static final Logger LOG = Logger.getInstance(Lesson.class); 17 | int id; 18 | @SerializedName("steps") 19 | List stepsId; 20 | 21 | public String title; 22 | private String lessonName; 23 | 24 | public transient Map steps = new HashMap<>(); 25 | private int lessonNo; 26 | 27 | public void build(int lessonNo, String courseDir, String sectionDir, Project project) { 28 | this.lessonNo = lessonNo; 29 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 30 | 31 | List stepsList = StepikConnector.getSteps(Utils.getIdQuery(stepsId), project); 32 | 33 | for (Step step : stepsList) { 34 | if (step.isCode()) { 35 | steps.put(step.position, step); 36 | String filename = "Step" + step.position; 37 | String path = getPath(courseDir, sectionDir, lessonName) + filename + ".java"; 38 | StepInfo tmp = new StepInfo(Integer.toString(step.id), sectionDir + "." + lessonName, filename, step.getText()); 39 | projectService.addStepInfo(path, tmp); 40 | } 41 | } 42 | } 43 | 44 | public void setLessonName(String lessonName) { 45 | this.lessonName = lessonName; 46 | } 47 | 48 | private String getPath(String... titles) { 49 | StringBuilder sb = new StringBuilder(); 50 | for (String title : titles) { 51 | sb.append(title + "/"); 52 | } 53 | return sb.toString(); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "\n\tLesson{" + 59 | "id=" + id + 60 | ", steps=" + steps + 61 | '}'; 62 | } 63 | 64 | @Override 65 | public boolean equals(Object o) { 66 | if (this == o) return true; 67 | if (o == null || getClass() != o.getClass()) return false; 68 | 69 | Lesson lesson = (Lesson) o; 70 | 71 | if (id != lesson.id) return false; 72 | if (lessonNo != lesson.lessonNo) return false; 73 | if (stepsId != null ? !stepsId.equals(lesson.stepsId) : lesson.stepsId != null) return false; 74 | if (title != null ? !title.equals(lesson.title) : lesson.title != null) return false; 75 | if (lessonName != null ? !lessonName.equals(lesson.lessonName) : lesson.lessonName != null) return false; 76 | return steps != null ? steps.equals(lesson.steps) : lesson.steps == null; 77 | 78 | } 79 | 80 | @Override 81 | public int hashCode() { 82 | int result = id; 83 | result = 31 * result + (stepsId != null ? stepsId.hashCode() : 0); 84 | result = 31 * result + (title != null ? title.hashCode() : 0); 85 | result = 31 * result + (lessonName != null ? lessonName.hashCode() : 0); 86 | result = 31 * result + (steps != null ? steps.hashCode() : 0); 87 | result = 31 * result + lessonNo; 88 | return result; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Section.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.intellij.openapi.project.Project; 5 | import org.stepik.plugin.stepikConnector.StepikConnector; 6 | import org.stepik.plugin.utils.Utils; 7 | import org.stepik.plugin.utils.YaTranslator; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class Section { 15 | int id; 16 | int course; 17 | public String position; 18 | public String title; 19 | private String sectionName; 20 | 21 | @SerializedName("units") 22 | List unitsId; 23 | public transient Map lessons = new HashMap<>(); 24 | 25 | private int sectionNo; 26 | 27 | 28 | public void build(int sectionNo, String courseDir, Project project) { 29 | this.sectionNo = sectionNo; 30 | int lessonNo = 0; 31 | 32 | List units = StepikConnector.getUnits(Utils.getIdQuery(unitsId), project); 33 | if (units == null) return; 34 | List lessonsId = new ArrayList<>(); 35 | List lessonNames = new ArrayList<>(); 36 | units.forEach(x -> lessonsId.add(x.getLessonId())); 37 | List lessons = StepikConnector.getLessons(Utils.getIdQuery(lessonsId), project); 38 | lessons.forEach(x -> lessonNames.add(x.title)); 39 | 40 | List newLessonNames = YaTranslator.translateNames(lessonNames, "lesson", project); 41 | 42 | for (Lesson lesson : lessons) { 43 | lesson.setLessonName(newLessonNames.get(lessonNo)); 44 | this.lessons.put(++lessonNo, lesson); 45 | lesson.build(lessonNo, courseDir, sectionName, project); 46 | } 47 | } 48 | 49 | public String getSectionName() { 50 | return sectionName; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "\nSection{" + 56 | "id=" + id + 57 | ", title='" + title + '\'' + 58 | ", lessons=" + lessons + 59 | '}'; 60 | } 61 | 62 | public void setSectionName(String sectionName) { 63 | this.sectionName = sectionName; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | if (o == null || getClass() != o.getClass()) return false; 70 | 71 | Section section = (Section) o; 72 | 73 | if (id != section.id) return false; 74 | if (course != section.course) return false; 75 | if (sectionNo != section.sectionNo) return false; 76 | if (position != null ? !position.equals(section.position) : section.position != null) return false; 77 | if (title != null ? !title.equals(section.title) : section.title != null) return false; 78 | if (sectionName != null ? !sectionName.equals(section.sectionName) : section.sectionName != null) return false; 79 | if (unitsId != null ? !unitsId.equals(section.unitsId) : section.unitsId != null) return false; 80 | return lessons != null ? lessons.equals(section.lessons) : section.lessons == null; 81 | 82 | } 83 | 84 | @Override 85 | public int hashCode() { 86 | int result = id; 87 | result = 31 * result + course; 88 | result = 31 * result + (position != null ? position.hashCode() : 0); 89 | result = 31 * result + (title != null ? title.hashCode() : 0); 90 | result = 31 * result + (sectionName != null ? sectionName.hashCode() : 0); 91 | result = 31 * result + (unitsId != null ? unitsId.hashCode() : 0); 92 | result = 31 * result + (lessons != null ? lessons.hashCode() : 0); 93 | result = 31 * result + sectionNo; 94 | return result; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Step.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import java.util.Map; 4 | 5 | public class Step { 6 | int id; 7 | int lesson; 8 | int position; 9 | Map block; 10 | 11 | public boolean isCode() { 12 | String tmp = (String) block.get("name"); 13 | return tmp.equals("code"); 14 | } 15 | 16 | public String getText() { 17 | return (String) block.get("text"); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "\n\t\tStep{" + 23 | "position=" + position + 24 | '}'; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | if (o == null || getClass() != o.getClass()) return false; 31 | 32 | Step step = (Step) o; 33 | 34 | if (id != step.id) return false; 35 | if (lesson != step.lesson) return false; 36 | if (position != step.position) return false; 37 | return block != null ? block.equals(step.block) : step.block == null; 38 | 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | int result = id; 44 | result = 31 * result + lesson; 45 | result = 31 * result + position; 46 | result = 31 * result + (block != null ? block.hashCode() : 0); 47 | return result; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/StepInfo.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import java.io.Serializable; 4 | 5 | public class StepInfo implements Serializable { 6 | private String stepID; 7 | private String attemptID; 8 | private String packageName; 9 | private String filename; 10 | private String submissionID; 11 | private String text; 12 | 13 | // public static final StepInfo EMPTY = new StepInfo(""); 14 | 15 | 16 | private void setAllEmpy() { 17 | stepID = ""; 18 | attemptID = ""; 19 | packageName = ""; 20 | filename = ""; 21 | submissionID = ""; 22 | text = ""; 23 | } 24 | 25 | public StepInfo() { 26 | setAllEmpy(); 27 | } 28 | 29 | public StepInfo(String stepID) { 30 | setAllEmpy(); 31 | this.stepID = stepID; 32 | } 33 | 34 | public StepInfo(String stepID, String packageName, String filename) { 35 | setAllEmpy(); 36 | this.stepID = stepID; 37 | this.packageName = packageName; 38 | this.filename = filename; 39 | } 40 | 41 | public StepInfo(String stepID, String packageName, String filename, String text) { 42 | setAllEmpy(); 43 | this.stepID = stepID; 44 | this.packageName = packageName; 45 | this.filename = filename; 46 | this.text = text; 47 | } 48 | 49 | public String getStepID() { 50 | return stepID; 51 | } 52 | 53 | public void setStepID(String stepID) { 54 | this.stepID = stepID; 55 | } 56 | 57 | public String getAttemptID() { 58 | return attemptID; 59 | } 60 | 61 | public void setAttemptID(String attemptID) { 62 | this.attemptID = attemptID; 63 | } 64 | 65 | public String getPackageName() { 66 | return packageName; 67 | } 68 | 69 | public void setPackageName(String packageName) { 70 | this.packageName = packageName; 71 | } 72 | 73 | public String getSubmissionID() { 74 | return submissionID; 75 | } 76 | 77 | public void setSubmissionID(String submissionID) { 78 | this.submissionID = submissionID; 79 | } 80 | 81 | public String getText() { 82 | return text; 83 | } 84 | 85 | public void setText(String text) { 86 | this.text = text; 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | return "StepInfo{" + 92 | "stepID='" + stepID + '\'' + 93 | ", attemptID='" + attemptID + '\'' + 94 | ", packageName='" + packageName + '\'' + 95 | '}'; 96 | } 97 | 98 | public String getFilename() { 99 | return filename; 100 | } 101 | 102 | public void setFilename(String filename) { 103 | this.filename = filename; 104 | } 105 | 106 | @Override 107 | public boolean equals(Object o) { 108 | if (this == o) return true; 109 | if (o == null || getClass() != o.getClass()) return false; 110 | 111 | StepInfo stepInfo = (StepInfo) o; 112 | 113 | if (stepID != null ? !stepID.equals(stepInfo.stepID) : stepInfo.stepID != null) return false; 114 | if (attemptID != null ? !attemptID.equals(stepInfo.attemptID) : stepInfo.attemptID != null) return false; 115 | if (packageName != null ? !packageName.equals(stepInfo.packageName) : stepInfo.packageName != null) 116 | return false; 117 | if (filename != null ? !filename.equals(stepInfo.filename) : stepInfo.filename != null) return false; 118 | if (submissionID != null ? !submissionID.equals(stepInfo.submissionID) : stepInfo.submissionID != null) 119 | return false; 120 | return text != null ? text.equals(stepInfo.text) : stepInfo.text == null; 121 | 122 | } 123 | 124 | @Override 125 | public int hashCode() { 126 | int result = stepID != null ? stepID.hashCode() : 0; 127 | result = 31 * result + (attemptID != null ? attemptID.hashCode() : 0); 128 | result = 31 * result + (packageName != null ? packageName.hashCode() : 0); 129 | result = 31 * result + (filename != null ? filename.hashCode() : 0); 130 | result = 31 * result + (submissionID != null ? submissionID.hashCode() : 0); 131 | result = 31 * result + (text != null ? text.hashCode() : 0); 132 | return result; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Submission.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import java.util.Map; 4 | 5 | public class Submission { 6 | public int id; 7 | private String status; 8 | private String time; 9 | private String hint; 10 | private Map reply; 11 | 12 | public String getStatus() { 13 | return status; 14 | } 15 | 16 | public String getTime() { 17 | return time; 18 | } 19 | 20 | public Map getReply() { 21 | return reply; 22 | } 23 | 24 | public String getCode() { 25 | return reply.get("code"); 26 | } 27 | 28 | public String getHint() { 29 | return hint; 30 | } 31 | 32 | 33 | @Override 34 | public boolean equals(Object o) { 35 | if (this == o) return true; 36 | if (o == null || getClass() != o.getClass()) return false; 37 | 38 | Submission that = (Submission) o; 39 | 40 | if (id != that.id) return false; 41 | if (status != null ? !status.equals(that.status) : that.status != null) return false; 42 | if (time != null ? !time.equals(that.time) : that.time != null) return false; 43 | if (hint != null ? !hint.equals(that.hint) : that.hint != null) return false; 44 | return reply != null ? reply.equals(that.reply) : that.reply == null; 45 | 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | int result = id; 51 | result = 31 * result + (status != null ? status.hashCode() : 0); 52 | result = 31 * result + (time != null ? time.hashCode() : 0); 53 | result = 31 * result + (hint != null ? hint.hashCode() : 0); 54 | result = 31 * result + (reply != null ? reply.hashCode() : 0); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/TokenInfo.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | public class TokenInfo { 4 | public String refresh_token; 5 | public String token_type; 6 | public String access_token; 7 | public String scope; 8 | public int expires_in; 9 | 10 | @Override 11 | public boolean equals(Object o) { 12 | if (this == o) return true; 13 | if (o == null || getClass() != o.getClass()) return false; 14 | 15 | TokenInfo tokenInfo = (TokenInfo) o; 16 | 17 | if (expires_in != tokenInfo.expires_in) return false; 18 | if (refresh_token != null ? !refresh_token.equals(tokenInfo.refresh_token) : tokenInfo.refresh_token != null) 19 | return false; 20 | if (token_type != null ? !token_type.equals(tokenInfo.token_type) : tokenInfo.token_type != null) return false; 21 | if (access_token != null ? !access_token.equals(tokenInfo.access_token) : tokenInfo.access_token != null) 22 | return false; 23 | return scope != null ? scope.equals(tokenInfo.scope) : tokenInfo.scope == null; 24 | 25 | } 26 | 27 | @Override 28 | public int hashCode() { 29 | int result = refresh_token != null ? refresh_token.hashCode() : 0; 30 | result = 31 * result + (token_type != null ? token_type.hashCode() : 0); 31 | result = 31 * result + (access_token != null ? access_token.hashCode() : 0); 32 | result = 31 * result + (scope != null ? scope.hashCode() : 0); 33 | result = 31 * result + expires_in; 34 | return result; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/modules/Unit.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.modules; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class Unit { 6 | int id; 7 | @SerializedName("lesson") 8 | private int lessonId; 9 | 10 | public int getLessonId() { 11 | return lessonId; 12 | } 13 | 14 | @Override 15 | public boolean equals(Object o) { 16 | if (this == o) return true; 17 | if (o == null || getClass() != o.getClass()) return false; 18 | 19 | Unit unit = (Unit) o; 20 | 21 | if (id != unit.id) return false; 22 | return lessonId == unit.lessonId; 23 | 24 | } 25 | 26 | @Override 27 | public int hashCode() { 28 | int result = id; 29 | result = 31 * result + lessonId; 30 | return result; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/projectWizard/StepikModuleBuilder.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.projectWizard; 2 | 3 | import com.intellij.ide.util.PropertiesComponent; 4 | import com.intellij.ide.util.projectWizard.JavaModuleBuilder; 5 | import com.intellij.ide.util.projectWizard.ModuleWizardStep; 6 | import com.intellij.ide.util.projectWizard.WizardContext; 7 | import com.intellij.openapi.diagnostic.Logger; 8 | import com.intellij.openapi.module.ModuleType; 9 | import com.intellij.openapi.options.ConfigurationException; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.openapi.roots.ModifiableRootModel; 12 | import com.intellij.openapi.roots.ui.configuration.ModulesProvider; 13 | import com.intellij.openapi.util.Pair; 14 | import com.intellij.openapi.vfs.VirtualFile; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.stepik.plugin.modules.Course; 17 | import org.stepik.plugin.modules.StepInfo; 18 | import org.stepik.plugin.stepikConnector.StepikConnector; 19 | import org.stepik.plugin.storages.ActionVisibleProperties; 20 | import org.stepik.plugin.storages.CourseDefinitionStorage; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | import java.nio.charset.Charset; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.nio.file.Paths; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | public class StepikModuleBuilder extends JavaModuleBuilder { 32 | private static final Logger LOG = Logger.getInstance(StepikModuleBuilder.class); 33 | 34 | @Override 35 | public void setupRootModel(ModifiableRootModel rootModel) throws ConfigurationException { 36 | Project project = rootModel.getProject(); 37 | final VirtualFile root = project.getBaseDir(); 38 | 39 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 40 | 41 | PropertiesComponent props = PropertiesComponent.getInstance(); 42 | projectService.setTranslate(Boolean.parseBoolean(props.getValue("translate"))); 43 | // projectService.setTranslate(props.getBoolean("translate")); 44 | projectService.setCourseID(props.getValue("courseId")); 45 | projectService.setProjectName(project.getName()); 46 | 47 | ActionVisibleProperties visibleProperties = ActionVisibleProperties.getInstance(project); 48 | visibleProperties.setEnabled(true); 49 | visibleProperties.setVisible(true); 50 | 51 | StepikConnector.setLoginAndPassword(props.getValue("login"), props.getValue("password"), project); 52 | LOG.debug("login = " + props.getValue("login")); 53 | 54 | StepikConnector.initToken(project); 55 | String courseId = projectService.getCourseID(); 56 | LOG.debug("build course structure " + root.getPath()); 57 | 58 | Course course = StepikConnector.getCourse(courseId, project); 59 | course.build(root.getPath(), rootModel.getProject()); 60 | 61 | projectService.mapPathInfo.entrySet().forEach(x -> { 62 | String path = x.getKey(); 63 | StepInfo stepInfo = x.getValue(); 64 | 65 | File file = new File(path); 66 | file.getParentFile().mkdirs(); 67 | try { 68 | Path path1 = Paths.get(path); 69 | Files.write(path1, getText(stepInfo.getFilename(), stepInfo.getPackageName()), Charset.forName("UTF-8")); 70 | } catch (IOException e) { 71 | LOG.error("Create file error\n" + e.getMessage()); 72 | } 73 | }); 74 | 75 | addSourcePath(Pair.create(root.getPath() + "/" + course.getName(project), "")); 76 | 77 | super.setupRootModel(rootModel); 78 | } 79 | 80 | public static List getText(String name, String pack) { 81 | List text = new ArrayList<>(); 82 | text.add("package " + pack + ";\n"); 83 | text.add("class " + name + " {"); 84 | text.add("\tpublic static void main(String[] args){"); 85 | text.add("\t\t//Write your code here"); 86 | text.add("\t}\n}"); 87 | return text; 88 | } 89 | 90 | public ModuleType getModuleType() { 91 | return StepikModuleType.STEPIK_MODULE_TYPE; 92 | } 93 | 94 | @Override 95 | public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, 96 | @NotNull ModulesProvider modulesProvider) { 97 | return new StepikModuleWizardStep[]{new StepikModuleWizardStep(this, wizardContext)}; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/projectWizard/StepikModuleType.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.projectWizard; 2 | 3 | import com.intellij.ide.util.projectWizard.*; 4 | import com.intellij.openapi.module.Module; 5 | import com.intellij.openapi.module.ModuleType; 6 | import com.intellij.openapi.module.ModuleTypeManager; 7 | import com.intellij.openapi.projectRoots.Sdk; 8 | import com.intellij.openapi.roots.ModuleRootManager; 9 | import com.intellij.openapi.roots.ui.configuration.ModulesProvider; 10 | import com.intellij.psi.CommonClassNames; 11 | import com.intellij.psi.JavaPsiFacade; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; 15 | import org.stepik.plugin.icons.PluginIcons; 16 | 17 | import javax.swing.*; 18 | 19 | public class StepikModuleType extends ModuleType { 20 | public static final String MODULE_NAME = "Stepik"; 21 | public static final StepikModuleType STEPIK_MODULE_TYPE; 22 | 23 | static { 24 | STEPIK_MODULE_TYPE = (StepikModuleType) instantiate("org.stepik.plugin.projectWizard.StepikModuleType"); 25 | } 26 | 27 | private static final String ID = "STEPIK_MODULE_TYPE"; 28 | 29 | public StepikModuleType() { 30 | super(ID); 31 | } 32 | 33 | public static StepikModuleType getInstance() { 34 | return (StepikModuleType) ModuleTypeManager.getInstance().findByID(ID); 35 | } 36 | 37 | @NotNull 38 | @Override 39 | public StepikModuleBuilder createModuleBuilder() { 40 | return new StepikModuleBuilder(); 41 | } 42 | 43 | @NotNull 44 | @Override 45 | public String getName() { 46 | return MODULE_NAME; 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public String getDescription() { 52 | return "Stepik Module Type"; 53 | } 54 | 55 | @Override 56 | public Icon getBigIcon() { 57 | return PluginIcons.STEPIK_LOGO; 58 | } 59 | 60 | @Override 61 | public Icon getNodeIcon(@Deprecated boolean b) { 62 | return PluginIcons.STEPIK_LOGO; 63 | } 64 | 65 | @NotNull 66 | @Override 67 | public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull StepikModuleBuilder moduleBuilder, @NotNull ModulesProvider modulesProvider) { 68 | // return super.createWizardSteps(wizardContext, moduleBuilder, modulesProvider); 69 | return new StepikModuleWizardStep[]{new StepikModuleWizardStep(moduleBuilder, wizardContext)}; 70 | } 71 | 72 | @NotNull 73 | private static ModuleType instantiate(String className) { 74 | try { 75 | return (ModuleType) Class.forName(className).newInstance(); 76 | } catch (Exception e) { 77 | throw new IllegalArgumentException(e); 78 | } 79 | } 80 | 81 | @Nullable 82 | @Override 83 | public ModuleWizardStep modifyProjectTypeStep(@NotNull SettingsStep settingsStep, @NotNull final ModuleBuilder moduleBuilder) { 84 | return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, moduleBuilder, moduleBuilder::isSuitableSdkType); 85 | } 86 | 87 | @Override 88 | public boolean isValidSdk(@NotNull final Module module, final Sdk projectSdk) { 89 | return isValidJavaSdk(module); 90 | } 91 | 92 | private static boolean isValidJavaSdk(@NotNull Module module) { 93 | if (ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.SOURCES).isEmpty()) 94 | return true; 95 | return JavaPsiFacade.getInstance(module.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, 96 | module.getModuleWithLibrariesScope()) != null; 97 | } 98 | } -------------------------------------------------------------------------------- /src/org/stepik/plugin/projectWizard/StepikModuleWizardStep.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/projectWizard/StepikModuleWizardStep.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.projectWizard; 2 | 3 | import com.intellij.ide.util.PropertiesComponent; 4 | import com.intellij.ide.util.projectWizard.ModuleWizardStep; 5 | import com.intellij.ide.util.projectWizard.WizardContext; 6 | import com.intellij.openapi.externalSystem.model.project.ProjectData; 7 | import com.intellij.openapi.project.Project; 8 | import org.stepik.plugin.storages.StepikApplicationStorage; 9 | import org.stepik.plugin.utils.Utils; 10 | 11 | import javax.swing.*; 12 | 13 | public class StepikModuleWizardStep extends ModuleWizardStep { 14 | private JPanel panel1; 15 | private JTextField textField1; 16 | private JPasswordField passwordField1; 17 | private JTextField courseLinkFiled; 18 | private JCheckBox CheckBox; 19 | 20 | private final Project myProjectOrNull; 21 | private final StepikModuleBuilder myBuilder; 22 | private final WizardContext myContext; 23 | private ProjectData myParent; 24 | 25 | public StepikModuleWizardStep(StepikModuleBuilder builder, WizardContext context) { 26 | myProjectOrNull = context.getProject(); 27 | myBuilder = builder; 28 | myContext = context; 29 | 30 | initComponents(); 31 | } 32 | 33 | private void initComponents() { 34 | panel1.setVisible(true); 35 | CheckBox.setSelected(true); 36 | } 37 | 38 | @Override 39 | public JComponent getComponent() { 40 | return panel1; 41 | } 42 | 43 | public String getCourseLink() { 44 | return courseLinkFiled.getText(); 45 | } 46 | 47 | @Override 48 | public void updateDataModel() { 49 | 50 | } 51 | 52 | @Override 53 | public void onStepLeaving() { 54 | saveSettings(); 55 | } 56 | 57 | private void saveSettings() { 58 | StepikApplicationStorage ws = StepikApplicationStorage.getInstance(); 59 | ws.setLogin(textField1.getText()); 60 | ws.setPassword(new String(passwordField1.getPassword())); 61 | 62 | PropertiesComponent props = PropertiesComponent.getInstance(); 63 | props.setValue("translate", Boolean.toString(CheckBox.isSelected())); 64 | props.setValue("courseId", Utils.parseUrl(courseLinkFiled.getText())); 65 | saveValue("login", textField1.getText()); 66 | saveValue("password", new String(passwordField1.getPassword())); 67 | } 68 | 69 | private static void saveValue(String key, String value) { 70 | PropertiesComponent props = PropertiesComponent.getInstance(); 71 | props.setValue(key, value); 72 | } 73 | 74 | private void createUIComponents() { 75 | // TODO: place custom component creation code here 76 | StepikApplicationStorage ws = StepikApplicationStorage.getInstance(); 77 | textField1 = new JTextField(ws.getLogin()); 78 | passwordField1 = new JPasswordField(ws.getPassword()); 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/projectWizard/StepikProjectStructureDetector.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.projectWizard; 2 | 3 | import com.intellij.ide.util.projectWizard.importSources.impl.JavaProjectStructureDetector; 4 | 5 | public class StepikProjectStructureDetector extends JavaProjectStructureDetector { 6 | } 7 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/stepikConnector/StepikConnector.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.stepikConnector; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.util.Pair; 9 | import com.intellij.util.net.HttpConfigurable; 10 | import com.mashape.unirest.http.HttpResponse; 11 | import com.mashape.unirest.http.JsonNode; 12 | import com.mashape.unirest.http.Unirest; 13 | import com.mashape.unirest.http.exceptions.UnirestException; 14 | import org.apache.http.HttpHost; 15 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 16 | import org.apache.http.impl.client.CloseableHttpClient; 17 | import org.apache.http.impl.client.HttpClients; 18 | import org.json.JSONObject; 19 | import org.stepik.plugin.storages.StudentStorage; 20 | import org.stepik.plugin.utils.NotificationTemplates; 21 | import org.stepik.plugin.utils.NotificationUtils; 22 | import org.stepik.plugin.modules.*; 23 | 24 | import javax.net.ssl.SSLContext; 25 | import javax.net.ssl.TrustManager; 26 | import javax.net.ssl.X509TrustManager; 27 | import java.security.KeyManagementException; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.SecureRandom; 30 | import java.security.cert.X509Certificate; 31 | import java.util.Date; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | public class StepikConnector { 37 | 38 | private static final String TOKEN_URL = "https://stepik.org/oauth2/token/"; 39 | private static final String API_URL = "https://stepik.org/api/"; 40 | private static final String CLIENT_ID = "hUCWcq3hZHCmz0DKrDtwOWITLcYutzot7p4n59vU"; 41 | 42 | private static final Logger logger = Logger.getInstance(StepikConnector.class); 43 | private static boolean isInstanceProperty = false; 44 | private static final Gson GSON = new GsonBuilder() 45 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 46 | .create(); 47 | 48 | public static void setSSLProperty(Project project) { 49 | // Create a trust manager that does not validate certificate for this connection 50 | TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { 51 | public X509Certificate[] getAcceptedIssuers() { 52 | return null; 53 | } 54 | 55 | public void checkClientTrusted(X509Certificate[] certs, String authType) { 56 | } 57 | 58 | public void checkServerTrusted(X509Certificate[] certs, String authType) { 59 | } 60 | }}; 61 | SSLContext sslContext = null; 62 | try { 63 | sslContext = SSLContext.getInstance("TLS"); 64 | sslContext.init(null, trustAllCerts, new SecureRandom()); 65 | } catch (NoSuchAlgorithmException | KeyManagementException e) { 66 | NotificationUtils.initRuntimeException(NotificationTemplates.CERTIFICATE_ERROR, project); 67 | } 68 | 69 | HttpConfigurable instance = HttpConfigurable.getInstance(); 70 | CloseableHttpClient httpClient; 71 | 72 | SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); 73 | if (instance.USE_HTTP_PROXY) { 74 | HttpHost host = new HttpHost(instance.PROXY_HOST, instance.PROXY_PORT); 75 | httpClient = HttpClients.custom() 76 | .setSSLSocketFactory(socketFactory) 77 | .setProxy(host) 78 | .build(); 79 | } else { 80 | httpClient = HttpClients.custom() 81 | .setSSLSocketFactory(socketFactory) 82 | .build(); 83 | } 84 | 85 | Unirest.setHttpClient(httpClient); 86 | } 87 | 88 | public static void initToken(Project project) { 89 | if (!isInstanceProperty) { 90 | setSSLProperty(project); 91 | isInstanceProperty = true; 92 | } 93 | 94 | setTokenGRP(project); 95 | } 96 | 97 | private static void setTokenGRP(Project project) { 98 | StudentStorage ws = StudentStorage.getInstance(project); 99 | String user = ws.getLogin(); 100 | String pass = ws.getPassword(); 101 | 102 | Map parameters = new HashMap<>(); 103 | parameters.put("grant_type", "password"); 104 | parameters.put("username", user); 105 | parameters.put("password", pass); 106 | parameters.put("client_id", CLIENT_ID); 107 | 108 | TokenInfo tokenInfo = postToStepikMapLinkReset(TOKEN_URL, parameters, TokenInfo.class, project); 109 | 110 | String token = tokenInfo.access_token; 111 | if (token != null && !token.isEmpty()) { 112 | ws.setToken(tokenInfo.access_token); 113 | ws.setRefresh_token(tokenInfo.refresh_token); 114 | } 115 | } 116 | 117 | private static T getFromStepik(String link, final Class container, Project project) { 118 | 119 | String token = getToken(project); 120 | HttpResponse response = null; 121 | try { 122 | response = Unirest 123 | .get(API_URL + link) 124 | .header("Authorization", "Bearer " + token) 125 | .asString(); 126 | } catch (UnirestException e) { 127 | NotificationUtils.initRuntimeException(project, e.getMessage()); 128 | } 129 | 130 | if (response.getStatus() != 200) { 131 | NotificationUtils.initRuntimeException(project, "Status " + Integer.toString(response.getStatus())); 132 | } 133 | final String responseString = response.getBody(); 134 | 135 | return GSON.fromJson(responseString, container); 136 | } 137 | 138 | private static T getFromStepik(String link, Map queryMap, final Class container, Project project) { 139 | 140 | String token = getToken(project); 141 | HttpResponse response = null; 142 | try { 143 | response = Unirest 144 | .get(API_URL + link) 145 | .header("Authorization", "Bearer " + token) 146 | .queryString(queryMap) 147 | .asString(); 148 | } catch (UnirestException e) { 149 | NotificationUtils.initRuntimeException(project); 150 | } 151 | final String responseString = response.getBody(); 152 | 153 | return GSON.fromJson(responseString, container); 154 | } 155 | 156 | private static T postToStepik(String link, JSONObject jsonObject, final Class container, Project project) { 157 | HttpResponse response = null; 158 | String token = getToken(project); 159 | try { 160 | response = Unirest 161 | .post(API_URL + link) 162 | .header("Authorization", "Bearer " + token) 163 | .header("Content-Type", "application/json") 164 | .body(jsonObject) 165 | .asJson(); 166 | } catch (UnirestException e) { 167 | NotificationUtils.initRuntimeException(project); 168 | } 169 | final JSONObject responseJson = response.getBody().getObject(); 170 | 171 | return GSON.fromJson(responseJson.toString(), container); 172 | } 173 | 174 | private static T postToStepikMapLinkReset(String link, Map parameters, final Class container, Project project) { 175 | HttpResponse response = null; 176 | try { 177 | response = Unirest 178 | .post(link) 179 | .fields(parameters) 180 | .asString(); 181 | } catch (UnirestException e) { 182 | NotificationUtils.initRuntimeException(project); 183 | } 184 | final String responseString = response.getBody(); 185 | 186 | return GSON.fromJson(responseString, container); 187 | } 188 | 189 | public static AuthorWrapper getCurrentUser(Project project) { 190 | return getFromStepik("stepiks/1", AuthorWrapper.class, project); 191 | } 192 | 193 | public static String getUserName(Project project) { 194 | AuthorWrapper aw = getCurrentUser(project); 195 | return aw.users.get(0).getName(); 196 | } 197 | 198 | public static Course getCourse(String courseId, Project project) { 199 | final String url = "courses/" + courseId; 200 | return getFromStepik(url, CoursesContainer.class, project).courses.get(0); 201 | } 202 | 203 | public static List
getSections(String idsQuery, Project project) { 204 | final String url = "sections" + idsQuery; 205 | return getFromStepik(url, SectionsContainer.class, project).sections; 206 | } 207 | 208 | public static List getUnits(String idsQuery, Project project) { 209 | final String url = "units" + idsQuery; 210 | return getFromStepik(url, UnitsContainer.class, project).units; 211 | } 212 | 213 | public static List getLessons(String idsQuery, Project project) { 214 | final String url = "lessons" + idsQuery; 215 | return getFromStepik(url, LessonsContainer.class, project).lessons; 216 | } 217 | 218 | public static List getStatus(String submissionID, Project project) { 219 | final String url = "submissions/" + submissionID; 220 | return getFromStepik(url, SubmissionsContainer.class, project).submissions; 221 | } 222 | 223 | public static List getSteps(String stepIdQuery, Project project) { 224 | final String url = "steps" + stepIdQuery; 225 | return getFromStepik(url, StepsContainer.class, project).steps; 226 | } 227 | 228 | public static String getAttemptId(String stepId, Project project) { 229 | String attempts = "attempts"; 230 | JSONObject first = new JSONObject(); 231 | JSONObject second = new JSONObject(); 232 | 233 | first.put("step", stepId); 234 | second.put("attempt", first); 235 | 236 | List attemptsL = postToStepik(attempts, second, AttemptsContainer.class, project).attempts; 237 | if (attemptsL == null) { 238 | NotificationUtils.initRuntimeException(project); 239 | } 240 | int id = attemptsL.get(0).id; 241 | return Integer.toString(id); 242 | } 243 | 244 | public static String sendFile(String file, String att_id, Project project) { 245 | 246 | JSONObject wrapper = new JSONObject(); 247 | JSONObject submission = new JSONObject(); 248 | JSONObject reply = new JSONObject(); 249 | 250 | reply.put("language", "java8"); 251 | reply.put("code", file); 252 | 253 | submission.put("attempt", att_id); 254 | submission.put("reply", reply); 255 | 256 | wrapper.put("submission", submission); 257 | 258 | int id = postToStepik("submissions", wrapper, SubmissionsContainer.class, project).submissions.get(0).id; 259 | return Integer.toString(id); 260 | } 261 | 262 | public static List getStepStatus(String stepId, Pair pair, Project project) { 263 | 264 | Map queryMap = new HashMap<>(); 265 | queryMap.put("step", stepId); 266 | queryMap.put(pair.first, pair.second); 267 | 268 | return getFromStepik("submissions", queryMap, SubmissionsContainer.class, project).submissions; 269 | } 270 | 271 | public static void setLoginAndPassword(String login, String password, Project project) { 272 | StudentStorage storage = StudentStorage.getInstance(project); 273 | storage.setLoginAndPassword(login, password); 274 | } 275 | 276 | public static String getLogin(Project project) { 277 | return StudentStorage.getInstance(project).getLogin(); 278 | } 279 | 280 | public static void setLogin(String login, Project project) { 281 | StudentStorage.getInstance(project).setLogin(login); 282 | } 283 | 284 | public static class AuthorWrapper { 285 | public List users; 286 | } 287 | 288 | public static class CoursesContainer { 289 | public List courses; 290 | public Map meta; 291 | } 292 | 293 | public static class SectionsContainer { 294 | public List
sections; 295 | public Map meta; 296 | } 297 | 298 | public static class UnitsContainer { 299 | public List units; 300 | public Map meta; 301 | } 302 | 303 | public static class LessonsContainer { 304 | public List lessons; 305 | public Map meta; 306 | } 307 | 308 | public static class StepsContainer { 309 | public List steps; 310 | public Map meta; 311 | } 312 | 313 | public static class SubmissionsContainer { 314 | public List submissions; 315 | public Map meta; 316 | } 317 | 318 | public static class AttemptsContainer { 319 | public List attempts; 320 | public Map meta; 321 | } 322 | 323 | public static List getSubmissions(String stepId, Project project) { 324 | Map map = new HashMap<>(); 325 | map.put("step", stepId); 326 | return getFromStepik("submissions", map, SubmissionsContainer.class, project).submissions; 327 | } 328 | 329 | public static String getToken(Project project) { 330 | StudentStorage storage = StudentStorage.getInstance(project); 331 | long baseTime = storage.getTokenTimeCreate(); 332 | 333 | if (!timePassedLessThen(baseTime, new Date().getTime(), 32400L)) { // 9 hours in sec 334 | StepikConnector.initToken(project); 335 | } 336 | return storage.getToken(); 337 | } 338 | 339 | private static boolean timePassedLessThen(long base, long current, long sec) { 340 | long delta = current - base; 341 | return delta - sec * 1000L < 0L; 342 | } 343 | 344 | public static boolean isPasswordSet(Project project) { 345 | StudentStorage storage = StudentStorage.getInstance(project); 346 | String password = storage.getPassword(); 347 | return !(password == null || password.isEmpty()); 348 | } 349 | } 350 | 351 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/storages/ActionVisibleProperties.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.storages; 2 | 3 | import com.intellij.openapi.components.*; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.util.xmlb.XmlSerializerUtil; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | @State(name = "ActionVisibleProperties", storages = @Storage(id = "ActionVisibleProperties", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/ActionVisibleProperties.xml")) 9 | public class ActionVisibleProperties implements PersistentStateComponent { 10 | private boolean visible; 11 | private boolean enabled; 12 | 13 | 14 | private ActionVisibleProperties() { 15 | visible = false; 16 | enabled = false; 17 | } 18 | 19 | public static ActionVisibleProperties getInstance(Project project) { 20 | return ServiceManager.getService(project, ActionVisibleProperties.class); 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public ActionVisibleProperties getState() { 26 | return this; 27 | } 28 | 29 | @Override 30 | public void loadState(ActionVisibleProperties state) { 31 | XmlSerializerUtil.copyBean(state, this); 32 | } 33 | 34 | public boolean isVisible() { 35 | return visible; 36 | } 37 | 38 | public void setVisible(boolean visible) { 39 | this.visible = visible; 40 | } 41 | 42 | public boolean isEnabled() { 43 | return enabled; 44 | } 45 | 46 | public void setEnabled(boolean enabled) { 47 | this.enabled = enabled; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/storages/CourseDefinitionStorage.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.storages; 2 | 3 | import com.intellij.openapi.components.*; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.util.xmlb.XmlSerializer; 6 | import org.jdom.Element; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.stepik.plugin.modules.StepInfo; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Set; 14 | 15 | @State(name = "CourseDefinitionStorage", storages = @Storage(id = "CourseDefinitionStorage", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/CourseDefinitionStorage.xml")) 16 | public class CourseDefinitionStorage implements PersistentStateComponent { 17 | 18 | public Map mapPathInfo = new HashMap<>(); 19 | private String courseID; 20 | private boolean translate; 21 | private String projectName; 22 | 23 | public static final String MAIN_ELEMENT = "ProjectService"; 24 | 25 | public static CourseDefinitionStorage getInstance(@NotNull final Project project) { 26 | return ServiceManager.getService(project, CourseDefinitionStorage.class); 27 | } 28 | 29 | @Nullable 30 | @Override 31 | public Element getState() { 32 | Element el = new Element("projectService"); 33 | Element courseElement = new Element(MAIN_ELEMENT); 34 | XmlSerializer.serializeInto(this, courseElement); 35 | el.addContent(courseElement); 36 | return el; 37 | } 38 | 39 | @Override 40 | public void loadState(Element state) { 41 | final Element mainElement = state.getChild(MAIN_ELEMENT); 42 | if (mainElement != null) { 43 | final CourseDefinitionStorage projectService = XmlSerializer.deserialize(mainElement, CourseDefinitionStorage.class); 44 | if (projectService != null) { 45 | mapPathInfo = projectService.mapPathInfo; 46 | courseID = projectService.courseID; 47 | translate = projectService.translate; 48 | projectName = projectService.projectName; 49 | } 50 | } 51 | 52 | // there can be your init 53 | } 54 | 55 | private StepInfo getStepInfo(String path) { 56 | // return mapPathInfo.getOrDefault(path, StepInfo.EMPTY); 57 | return mapPathInfo.getOrDefault(path, new StepInfo("")); 58 | } 59 | 60 | private StepInfo getOrCreateStepInfo(String path) { 61 | if (mapPathInfo.containsKey(path)) { 62 | return mapPathInfo.get(path); 63 | } else { 64 | StepInfo stepInfo = new StepInfo(""); 65 | mapPathInfo.put(path, stepInfo); 66 | return stepInfo; 67 | } 68 | } 69 | 70 | public String getStepID(String path) { 71 | return getStepInfo(path).getStepID(); 72 | } 73 | 74 | public String getAttemptID(String path) { 75 | return getStepInfo(path).getAttemptID(); 76 | } 77 | 78 | public String getPackageName(String path) { 79 | return getStepInfo(path).getPackageName(); 80 | } 81 | 82 | public String getFilename(String path) { 83 | return getStepInfo(path).getFilename(); 84 | } 85 | 86 | public String getSubmissionID(String path) { 87 | return getStepInfo(path).getSubmissionID(); 88 | } 89 | 90 | public String getText(String path) { 91 | return getStepInfo(path).getText(); 92 | } 93 | 94 | public void setStepID(String path, String stepID) { 95 | StepInfo stepInfo = getOrCreateStepInfo(path); 96 | stepInfo.setStepID(stepID); 97 | } 98 | 99 | public void setAttemptID(String path, String attemptID) { 100 | StepInfo stepInfo = getOrCreateStepInfo(path); 101 | stepInfo.setAttemptID(attemptID); 102 | } 103 | 104 | public void setPackageName(String path, String packageName) { 105 | StepInfo stepInfo = getOrCreateStepInfo(path); 106 | stepInfo.setPackageName(packageName); 107 | } 108 | 109 | public void setFilename(String path, String filename) { 110 | StepInfo stepInfo = getOrCreateStepInfo(path); 111 | stepInfo.setFilename(filename); 112 | } 113 | 114 | public void setSubmissionID(String path, String submissionID) { 115 | StepInfo stepInfo = getOrCreateStepInfo(path); 116 | stepInfo.setSubmissionID(submissionID); 117 | } 118 | 119 | public void setText(String path, String text) { 120 | StepInfo stepInfo = getOrCreateStepInfo(path); 121 | stepInfo.setText(text); 122 | } 123 | 124 | public void addStepInfo(String path, StepInfo stepInfo) { 125 | mapPathInfo.put(path, stepInfo); 126 | } 127 | 128 | public String getCourseID() { 129 | return courseID; 130 | } 131 | 132 | public void setCourseID(String courseID) { 133 | this.courseID = courseID; 134 | } 135 | 136 | public boolean isTranslate() { 137 | return translate; 138 | } 139 | 140 | public void setTranslate(boolean translate) { 141 | this.translate = translate; 142 | } 143 | 144 | public String getProjectName() { 145 | return projectName; 146 | } 147 | 148 | public void setProjectName(String projectName) { 149 | this.projectName = projectName; 150 | } 151 | 152 | public Map getMapPathInfo() { 153 | return mapPathInfo; 154 | } 155 | 156 | public void removeAll(Set removed) { 157 | mapPathInfo.keySet().removeAll(removed); 158 | } 159 | 160 | public boolean contains(String path) { 161 | return mapPathInfo.containsKey(path); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/storages/StepikApplicationStorage.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.storages; 2 | 3 | import com.intellij.ide.passwordSafe.PasswordSafe; 4 | import com.intellij.ide.passwordSafe.PasswordSafeException; 5 | import com.intellij.openapi.components.*; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.util.text.StringUtil; 8 | import com.intellij.util.xmlb.XmlSerializerUtil; 9 | import com.intellij.util.xmlb.annotations.Transient; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | 13 | @State(name = "StepikApplicationStorage", storages = @Storage(id = "StepikApplicationStorage", file = StoragePathMacros.APP_CONFIG + "/StepikApplicationStorage.xml")) 14 | public class StepikApplicationStorage implements PersistentStateComponent { 15 | private final String clientId = "hUCWcq3hZHCmz0DKrDtwOWITLcYutzot7p4n59vU"; 16 | private String login; 17 | private long tokenTimeCreate; 18 | 19 | @Transient 20 | private static final Logger LOG = Logger.getInstance(StepikApplicationStorage.class); 21 | 22 | public static StepikApplicationStorage getInstance() { 23 | return ServiceManager.getService(StepikApplicationStorage.class); 24 | } 25 | 26 | public StepikApplicationStorage() { 27 | } 28 | 29 | public StepikApplicationStorage getState() { 30 | return this; 31 | } 32 | 33 | public void loadState(StepikApplicationStorage state) { 34 | XmlSerializerUtil.copyBean(state, this); 35 | } 36 | 37 | private String getPasswordKey() { 38 | return "STEPIK_SETTINGS_PASSWORD_KEY: " + getLogin(); 39 | } 40 | 41 | 42 | // setters and getters ------------------------------- 43 | 44 | @NotNull 45 | public String getPassword() { 46 | final String login = getLogin(); 47 | if (StringUtil.isEmptyOrSpaces(login)) return ""; 48 | 49 | String password; 50 | try { 51 | password = PasswordSafe.getInstance().getPassword(null, StepikApplicationStorage.class, getPasswordKey()); 52 | } catch (PasswordSafeException e) { 53 | LOG.info("Couldn't get password for key [" + getPasswordKey() + "]", e); 54 | password = ""; 55 | } 56 | 57 | return StringUtil.notNullize(password); 58 | } 59 | 60 | public void setPassword(@NotNull String password) { 61 | try { 62 | PasswordSafe.getInstance().storePassword(null, StepikApplicationStorage.class, getPasswordKey(), password); 63 | } catch (PasswordSafeException e) { 64 | LOG.info("Couldn't set password for key [" + getPasswordKey() + "]", e); 65 | } 66 | } 67 | 68 | public String getClientId() { 69 | return clientId; 70 | } 71 | 72 | public String getLogin() { 73 | return login; 74 | } 75 | 76 | public void setLogin(String login) { 77 | this.login = login; 78 | } 79 | 80 | public void setTokenTimeCreate(long tokenTimeCreate) { 81 | this.tokenTimeCreate = tokenTimeCreate; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/storages/StudentStorage.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.storages; 2 | 3 | import com.intellij.ide.passwordSafe.PasswordSafe; 4 | import com.intellij.ide.passwordSafe.PasswordSafeException; 5 | import com.intellij.openapi.components.*; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.util.text.StringUtil; 9 | import com.intellij.util.xmlb.XmlSerializerUtil; 10 | import com.intellij.util.xmlb.annotations.Transient; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Date; 14 | 15 | 16 | @State(name = "StudentStorage", storages = @Storage(id = "StudentStorage", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/StudentStorage.xml")) 17 | public class StudentStorage implements PersistentStateComponent { 18 | private String token; 19 | private String refresh_token; 20 | private String login; 21 | private long tokenTimeCreate; 22 | 23 | @Transient 24 | private static final Logger LOG = Logger.getInstance(StudentStorage.class); 25 | 26 | public static StudentStorage getInstance(Project project) { 27 | return ServiceManager.getService(project, StudentStorage.class); 28 | } 29 | 30 | public StudentStorage() { 31 | } 32 | 33 | public StudentStorage getState() { 34 | return this; 35 | } 36 | 37 | public void loadState(StudentStorage state) { 38 | XmlSerializerUtil.copyBean(state, this); 39 | } 40 | 41 | private String getPasswordKey() { 42 | return "STEPIK_SETTINGS_PASSWORD_KEY: " + getLogin(); 43 | } 44 | 45 | 46 | // setters and getters ------------------------------- 47 | 48 | @NotNull 49 | public String getPassword() { 50 | final String login = getLogin(); 51 | if (StringUtil.isEmptyOrSpaces(login)) return ""; 52 | 53 | String password; 54 | try { 55 | password = PasswordSafe.getInstance().getPassword(null, StudentStorage.class, getPasswordKey()); 56 | } catch (PasswordSafeException e) { 57 | LOG.info("Couldn't get password for key [" + getPasswordKey() + "]", e); 58 | password = ""; 59 | } 60 | 61 | return StringUtil.notNullize(password); 62 | } 63 | 64 | private void setPassword(@NotNull String password) { 65 | try { 66 | PasswordSafe.getInstance().storePassword(null, StudentStorage.class, getPasswordKey(), password); 67 | } catch (PasswordSafeException e) { 68 | LOG.info("Couldn't set password for key [" + getPasswordKey() + "]", e); 69 | } 70 | } 71 | 72 | public String getToken() { 73 | return token; 74 | } 75 | 76 | private boolean timePassedLessThen(long base, long current, long sec) { 77 | // long delta = d1.getTime() - d0.getTime(); 78 | long delta = current - base; 79 | return delta - sec * 1000L < 0L; 80 | } 81 | 82 | public void setToken(String token) { 83 | tokenTimeCreate = new Date().getTime(); 84 | this.token = token; 85 | } 86 | 87 | public String getRefresh_token() { 88 | return refresh_token; 89 | } 90 | 91 | public void setRefresh_token(String refresh_token) { 92 | this.refresh_token = refresh_token; 93 | } 94 | 95 | public String getLogin() { 96 | return login; 97 | } 98 | 99 | public void setLogin(String login) { 100 | this.login = login; 101 | } 102 | 103 | public void setLoginAndPassword(String login, String password) { 104 | setLogin(login); 105 | setPassword(password); 106 | } 107 | 108 | public void setTokenTimeCreate(long tokenTimeCreate) { 109 | this.tokenTimeCreate = tokenTimeCreate; 110 | } 111 | 112 | public long getTokenTimeCreate() { 113 | return tokenTimeCreate; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/JavaStudyPluginConfigurator.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.actionSystem.DefaultActionGroup; 4 | import com.intellij.openapi.project.Project; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class JavaStudyPluginConfigurator extends StudyBasePluginConfigurator { 8 | 9 | @NotNull 10 | @Override 11 | public DefaultActionGroup getActionGroup(Project project) { 12 | final DefaultActionGroup baseGroup = super.getActionGroup(project); 13 | final DefaultActionGroup group = new DefaultActionGroup(); 14 | // group.add(new PyStudyCheckAction()); 15 | group.addAll(baseGroup); 16 | return group; 17 | } 18 | 19 | @NotNull 20 | @Override 21 | public String getDefaultHighlightingMode() { 22 | return "java"; 23 | } 24 | 25 | @NotNull 26 | @Override 27 | public String getLanguageScriptUrl() { 28 | return getClass().getResource("/python.js").toExternalForm(); 29 | } 30 | 31 | @Override 32 | public boolean accept(@NotNull Project project) { 33 | // StudyTaskManager taskManager = StudyTaskManager.getInstance(project); 34 | // if (taskManager == null) return false; 35 | // Course course = taskManager.getCourse(); 36 | // return course != null && "Python".equals(course.getLanguage()) && "PyCharm".equals(course.getCourseType()); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyBasePluginConfigurator.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.actionSystem.DefaultActionGroup; 4 | import com.intellij.openapi.fileEditor.FileEditorManager; 5 | import com.intellij.openapi.fileEditor.FileEditorManagerEvent; 6 | import com.intellij.openapi.fileEditor.FileEditorManagerListener; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.stepik.plugin.storages.CourseDefinitionStorage; 11 | 12 | import javax.swing.*; 13 | import java.util.Collections; 14 | import java.util.Map; 15 | 16 | public abstract class StudyBasePluginConfigurator implements StudyPluginConfigurator { 17 | @NotNull 18 | @Override 19 | public DefaultActionGroup getActionGroup(Project project) { 20 | final DefaultActionGroup group = new DefaultActionGroup(); 21 | // group.add(new StudyPreviousStudyTaskAction()); 22 | // group.add(new StudyNextStudyTaskAction()); 23 | // group.add(new StudyRefreshTaskFileAction()); 24 | // group.add(new StudyShowHintAction()); 25 | // 26 | // group.add(new StudyRunAction()); 27 | // group.add(new StudyEditInputAction()); 28 | return group; 29 | } 30 | 31 | @NotNull 32 | @Override 33 | public Map getAdditionalPanels(Project project) { 34 | return Collections.emptyMap(); 35 | } 36 | 37 | @NotNull 38 | @Override 39 | public FileEditorManagerListener getFileEditorManagerListener(@NotNull Project project, @NotNull StudyToolWindow toolWindow) { 40 | 41 | return new FileEditorManagerListener() { 42 | 43 | private final CourseDefinitionStorage cs = CourseDefinitionStorage.getInstance(project); 44 | private static final String EMPTY_STEP_TEXT = "Please, open any Step to see Step description"; 45 | 46 | @Override 47 | public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { 48 | if (cs.contains(file.getPath())) { 49 | setStepText(file); 50 | } 51 | } 52 | 53 | @Override 54 | public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { 55 | toolWindow.setStepText(EMPTY_STEP_TEXT); 56 | } 57 | 58 | @Override 59 | public void selectionChanged(@NotNull FileEditorManagerEvent event) { 60 | VirtualFile file = event.getNewFile(); 61 | if (file != null && cs.contains(file.getPath())) { 62 | setStepText(file); 63 | } 64 | } 65 | 66 | private void setStepText(final VirtualFile virtualFile) { 67 | String text = CourseDefinitionStorage.getInstance(project).getText(virtualFile.getPath()); 68 | toolWindow.setStepText(text); 69 | } 70 | }; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyCondition.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.project.DumbAware; 4 | import com.intellij.openapi.util.Condition; 5 | 6 | /** 7 | * author: liana 8 | * data: 7/29/14. 9 | */ 10 | public class StudyCondition implements Condition, DumbAware { 11 | @Override 12 | public boolean value(Object o) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyEditor.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; 4 | import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class StudyEditor extends PsiAwareTextEditorImpl { 10 | // public StudyEditor(@NotNull Project project, @NotNull VirtualFile virtualFile, TextEditorProvider textEditorProvider) { 11 | // super(project, virtualFile, textEditorProvider); 12 | // } 13 | // private final TaskFile myTaskFile; 14 | // private static final Map myDocumentListeners = new HashMap(); 15 | // 16 | // public TaskFile getTaskFile() { 17 | // return myTaskFile; 18 | // } 19 | 20 | // public static void addDocumentListener(@NotNull final Document document, @NotNull final EduDocumentListener listener) { 21 | // document.addDocumentListener(listener); 22 | // myDocumentListeners.put(document, listener); 23 | // } 24 | // 25 | public StudyEditor(@NotNull final Project project, @NotNull final VirtualFile file) { 26 | super(project, file, TextEditorProvider.getInstance()); 27 | // myTaskFile = StudyUtils.getTaskFile(project, file); 28 | } 29 | // 30 | // public static void removeListener(Document document) { 31 | // final EduDocumentListener listener = myDocumentListeners.get(document); 32 | // if (listener != null) { 33 | // document.removeDocumentListener(listener); 34 | // } 35 | // myDocumentListeners.remove(document); 36 | // } 37 | } 38 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyEditorFactoryListener.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.editor.Document; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.editor.LogicalPosition; 8 | import com.intellij.openapi.editor.event.EditorFactoryEvent; 9 | import com.intellij.openapi.editor.event.EditorFactoryListener; 10 | import com.intellij.openapi.editor.event.EditorMouseAdapter; 11 | import com.intellij.openapi.editor.event.EditorMouseEvent; 12 | import com.intellij.openapi.fileEditor.FileDocumentManager; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.openapi.vfs.VirtualFile; 15 | import com.intellij.openapi.wm.ToolWindow; 16 | import com.intellij.openapi.wm.ToolWindowManager; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import java.awt.*; 20 | 21 | 22 | public class StudyEditorFactoryListener implements EditorFactoryListener { 23 | 24 | private static class WindowSelectionListener extends EditorMouseAdapter { 25 | @Override 26 | public void mouseClicked(EditorMouseEvent e) { 27 | final Editor editor = e.getEditor(); 28 | final Point point = e.getMouseEvent().getPoint(); 29 | final LogicalPosition pos = editor.xyToLogicalPosition(point); 30 | } 31 | } 32 | 33 | @Override 34 | public void editorCreated(@NotNull final EditorFactoryEvent event) { 35 | final Editor editor = event.getEditor(); 36 | 37 | final Project project = editor.getProject(); 38 | if (project == null) { 39 | return; 40 | } 41 | ApplicationManager.getApplication().invokeLater( 42 | () -> ApplicationManager.getApplication().runWriteAction(() -> { 43 | final Document document = editor.getDocument(); 44 | final VirtualFile openedFile = FileDocumentManager.getInstance().getFile(document); 45 | if (openedFile != null) { 46 | final ToolWindow studyToolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); 47 | if (studyToolWindow != null) { 48 | StudyUtils.updateToolWindows(project); 49 | studyToolWindow.show(null); 50 | } 51 | } 52 | }) 53 | ); 54 | } 55 | 56 | @Override 57 | public void editorReleased(@NotNull EditorFactoryEvent event) { 58 | final Editor editor = event.getEditor(); 59 | final Document document = editor.getDocument(); 60 | // StudyEditor.removeListener(document); 61 | // editor.getMarkupModel().removeAllHighlighters(); 62 | // editor.getSelectionModel().removeSelection(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyJavaFxToolWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2015 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.stepik.plugin.toolWindow; 17 | 18 | import com.intellij.openapi.project.Project; 19 | import com.intellij.openapi.project.ProjectUtil; 20 | import org.stepik.plugin.utils.MyLogger; 21 | 22 | import javax.swing.*; 23 | 24 | public class StudyJavaFxToolWindow extends StudyToolWindow { 25 | private StudyBrowserWindow myBrowserWindow; 26 | 27 | public StudyJavaFxToolWindow() { 28 | super(); 29 | } 30 | 31 | @Override 32 | public JComponent createStepInfoPanel(String stepText, Project project) { 33 | MyLogger.getInstance().getLOG().debug("createStepInfoPanel"); 34 | myBrowserWindow = new StudyBrowserWindow(true, false); 35 | // myBrowserWindow.addBackAndOpenButtons(); 36 | JPanel panel = new JPanel(); 37 | panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 38 | panel.add(myBrowserWindow.getPanel()); 39 | return panel; 40 | } 41 | 42 | @Override 43 | public void setStepText(String text) { 44 | StudyPluginConfigurator configurator = StudyUtils.getConfigurator(ProjectUtil.guessCurrentProject(this)); 45 | myBrowserWindow.loadContent(text, configurator); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyPluginConfigurator.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.actionSystem.DefaultActionGroup; 4 | import com.intellij.openapi.extensions.ExtensionPointName; 5 | import com.intellij.openapi.fileEditor.FileEditorManagerListener; 6 | import com.intellij.openapi.project.Project; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import javax.swing.*; 10 | import java.util.Map; 11 | 12 | public interface StudyPluginConfigurator { 13 | ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.studyToolWindowConfigurator"); 14 | 15 | /** 16 | * Provide action group that should be placed on the tool window toolbar. 17 | * 18 | * @param project 19 | * @return 20 | */ 21 | @NotNull 22 | DefaultActionGroup getActionGroup(Project project); 23 | 24 | /** 25 | * Provide panels, that could be added to Task tool window. 26 | * 27 | * @param project 28 | * @return Map from panel id, i.e. "Task description", to panel itself. 29 | */ 30 | @NotNull 31 | Map getAdditionalPanels(Project project); 32 | 33 | @NotNull 34 | FileEditorManagerListener getFileEditorManagerListener(@NotNull final Project project, @NotNull final StudyToolWindow toolWindow); 35 | 36 | /** 37 | * @return parameter for CodeMirror script. Available languages: @see <@linktourl http://codemirror.net/mode/> 38 | */ 39 | @NotNull 40 | String getDefaultHighlightingMode(); 41 | 42 | @NotNull 43 | String getLanguageScriptUrl(); 44 | 45 | boolean accept(@NotNull final Project project); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudySwingToolWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2015 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.stepik.plugin.toolWindow; 17 | 18 | import com.intellij.openapi.editor.colors.EditorColorsManager; 19 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 20 | import com.intellij.openapi.project.Project; 21 | import com.intellij.ui.BrowserHyperlinkListener; 22 | import com.intellij.ui.ColorUtil; 23 | import com.intellij.ui.components.JBScrollPane; 24 | import com.intellij.util.ui.UIUtil; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import javax.swing.*; 28 | import javax.swing.border.EmptyBorder; 29 | import javax.swing.text.html.HTMLDocument; 30 | import javax.swing.text.html.HTMLEditorKit; 31 | import java.awt.*; 32 | 33 | public class StudySwingToolWindow extends StudyToolWindow { 34 | private JTextPane myTaskTextPane; 35 | 36 | public StudySwingToolWindow() { 37 | super(); 38 | } 39 | 40 | @Override 41 | public JComponent createStepInfoPanel(String stepText, Project project) { 42 | myTaskTextPane = new JTextPane(); 43 | final JBScrollPane scrollPane = new JBScrollPane(myTaskTextPane); 44 | myTaskTextPane.setContentType(new HTMLEditorKit().getContentType()); 45 | final EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme(); 46 | int fontSize = editorColorsScheme.getEditorFontSize(); 47 | final String fontName = editorColorsScheme.getEditorFontName(); 48 | final Font font = new Font(fontName, Font.PLAIN, fontSize); 49 | String bodyRule = "body { font-family: " + font.getFamily() + "; " + 50 | "font-size: " + font.getSize() + "pt; }" + 51 | "pre {font-family: Courier; display: inline; ine-height: 50px; padding-top: 5px; padding-bottom: 5px; padding-left: 5px; background-color:" 52 | + ColorUtil.toHex(ColorUtil.dimmer(UIUtil.getPanelBackground())) + ";}" + 53 | "code {font-family: Courier; display: flex; float: left; background-color:" 54 | + ColorUtil.toHex(ColorUtil.dimmer(UIUtil.getPanelBackground())) + ";}"; 55 | ((HTMLDocument) myTaskTextPane.getDocument()).getStyleSheet().addRule(bodyRule); 56 | myTaskTextPane.setEditable(false); 57 | if (!UIUtil.isUnderDarcula()) { 58 | myTaskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); 59 | } 60 | myTaskTextPane.setBorder(new EmptyBorder(20, 20, 0, 10)); 61 | myTaskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); 62 | return scrollPane; 63 | } 64 | 65 | public void setStepText(@NotNull String text) { 66 | // myTaskTextPane.setText("step_link\n" + text); 67 | myTaskTextPane.setText(text); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyToolWindow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2015 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.stepik.plugin.toolWindow; 17 | 18 | import com.intellij.openapi.Disposable; 19 | import com.intellij.openapi.actionSystem.DataProvider; 20 | import com.intellij.openapi.diagnostic.Logger; 21 | import com.intellij.openapi.fileEditor.FileEditorManagerListener; 22 | import com.intellij.openapi.project.DumbAware; 23 | import com.intellij.openapi.project.Project; 24 | import com.intellij.openapi.ui.SimpleToolWindowPanel; 25 | import com.intellij.ui.JBCardLayout; 26 | import com.intellij.ui.OnePixelSplitter; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.stepik.plugin.utils.MyLogger; 29 | 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.util.Map; 33 | 34 | public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable, DumbAware { 35 | private static final Logger LOG = Logger.getInstance(StudyToolWindow.class); 36 | private static final String STEP_INFO_ID = "stepInfo"; 37 | private final JBCardLayout myCardLayout; 38 | private final JPanel myContentPanel; 39 | private final OnePixelSplitter mySplitPane; 40 | 41 | public StudyToolWindow() { 42 | super(true, true); 43 | myCardLayout = new JBCardLayout(); 44 | myContentPanel = new JPanel(myCardLayout); 45 | mySplitPane = new OnePixelSplitter(myVertical = true); 46 | } 47 | 48 | public void init(Project project) { 49 | MyLogger.getInstance().getLOG().debug("init"); 50 | String stepText = StudyUtils.getStepText(project); 51 | if (stepText == null) return; 52 | 53 | // JPanel toolbarPanel = createToolbarPanel(project); 54 | // setToolbar(toolbarPanel); 55 | 56 | myContentPanel.add(STEP_INFO_ID, createStepInfoPanel(stepText, project)); 57 | mySplitPane.setFirstComponent(myContentPanel); 58 | addAdditionalPanels(project); 59 | myCardLayout.show(myContentPanel, STEP_INFO_ID); 60 | 61 | setContent(mySplitPane); 62 | 63 | StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project); 64 | if (configurator != null) { 65 | final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this); 66 | project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener); 67 | } 68 | 69 | setStepText(stepText); 70 | } 71 | 72 | private void addAdditionalPanels(Project project) { 73 | StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project); 74 | assert configurator != null; 75 | Map panels = configurator.getAdditionalPanels(project); 76 | for (Map.Entry entry : panels.entrySet()) { 77 | myContentPanel.add(entry.getKey(), entry.getValue()); 78 | } 79 | } 80 | 81 | public void dispose() { 82 | } 83 | 84 | //used in checkiO plugin. 85 | @SuppressWarnings("unused") 86 | public void showPanelById(@NotNull final String panelId) { 87 | myCardLayout.swipe(myContentPanel, panelId, JBCardLayout.SwipeDirection.AUTO); 88 | } 89 | 90 | //used in checkiO plugin. 91 | @SuppressWarnings("unused") 92 | public void setBottomComponent(JComponent component) { 93 | mySplitPane.setSecondComponent(component); 94 | } 95 | 96 | //used in checkiO plugin. 97 | @SuppressWarnings("unused") 98 | public JComponent getBottomComponent() { 99 | return mySplitPane.getSecondComponent(); 100 | } 101 | 102 | //used in checkiO plugin. 103 | @SuppressWarnings("unused") 104 | public void setTopComponentPrefferedSize(@NotNull final Dimension dimension) { 105 | myContentPanel.setPreferredSize(dimension); 106 | } 107 | 108 | //used in checkiO plugin. 109 | @SuppressWarnings("unused") 110 | public JPanel getContentPanel() { 111 | return myContentPanel; 112 | } 113 | 114 | 115 | public abstract JComponent createStepInfoPanel(String stepText, Project project); 116 | 117 | // private static JPanel createToolbarPanel(@NotNull final Project project) { 118 | // final DefaultActionGroup group = getActionGroup(project); 119 | 120 | // final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true); 121 | // return JBUI.Panels.simplePanel(actionToolBar.getComponent()); 122 | // } 123 | 124 | // private static DefaultActionGroup getActionGroup(@NotNull final Project project) { 125 | // Course course = StudyTaskManager.getInstance(project).getCourse(); 126 | // if (course == null) { 127 | // LOG.debug("Course is null"); 128 | // return new DefaultActionGroup(); 129 | // } 130 | // StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project); 131 | // assert configurator != null; 132 | // 133 | // return configurator.getActionGroup(project); 134 | // } 135 | 136 | public abstract void setStepText(String text); 137 | } 138 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.project.DumbAware; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.util.Disposer; 6 | import com.intellij.openapi.wm.ToolWindow; 7 | import com.intellij.openapi.wm.ToolWindowFactory; 8 | import com.intellij.ui.content.Content; 9 | import com.intellij.ui.content.ContentManager; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.stepik.plugin.icons.PluginIcons; 12 | import org.stepik.plugin.utils.MyLogger; 13 | 14 | public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { 15 | public static final String STUDY_TOOL_WINDOW = "Step Description"; 16 | 17 | 18 | @Override 19 | public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { 20 | MyLogger.getInstance().getLOG().debug("createToolWindowContent"); 21 | toolWindow.setIcon(PluginIcons.STEPIK_LOGO_MINI); 22 | 23 | final StudyToolWindow studyToolWindow; 24 | if (StudyUtils.hasJavaFx()) { 25 | studyToolWindow = new StudyJavaFxToolWindow(); 26 | } else { 27 | studyToolWindow = new StudySwingToolWindow(); 28 | } 29 | studyToolWindow.init(project); 30 | final ContentManager contentManager = toolWindow.getContentManager(); 31 | final Content content = contentManager.getFactory().createContent(studyToolWindow, null, false); 32 | contentManager.addContent(content); 33 | Disposer.register(project, studyToolWindow); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/toolWindow/StudyUtils.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.toolWindow; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.Presentation; 5 | import com.intellij.openapi.diagnostic.Logger; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.fileEditor.FileEditor; 8 | import com.intellij.openapi.fileEditor.FileEditorManager; 9 | import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.openapi.ui.popup.Balloon; 12 | import com.intellij.openapi.util.Disposer; 13 | import com.intellij.openapi.vfs.VirtualFile; 14 | import com.intellij.openapi.wm.ToolWindow; 15 | import com.intellij.openapi.wm.ToolWindowManager; 16 | import com.intellij.ui.awt.RelativePoint; 17 | import com.intellij.ui.content.Content; 18 | import com.intellij.util.ui.UIUtil; 19 | import org.jetbrains.annotations.NotNull; 20 | import org.jetbrains.annotations.Nullable; 21 | import org.stepik.plugin.storages.CourseDefinitionStorage; 22 | 23 | import javax.swing.*; 24 | import java.awt.*; 25 | import java.io.*; 26 | import java.util.Collection; 27 | 28 | public class StudyUtils { 29 | private StudyUtils() { 30 | } 31 | 32 | private static final Logger LOG = Logger.getInstance(StudyUtils.class.getName()); 33 | private static final String EMPTY_STEP_TEXT = "Please, open any Step to see Step description"; 34 | 35 | public static void closeSilently(@Nullable final Closeable stream) { 36 | if (stream != null) { 37 | try { 38 | stream.close(); 39 | } catch (IOException e) { 40 | // close silently 41 | } 42 | } 43 | } 44 | 45 | public static boolean isZip(String fileName) { 46 | return fileName.contains(".zip"); 47 | } 48 | 49 | public static T getFirst(@NotNull final Iterable container) { 50 | return container.iterator().next(); 51 | } 52 | 53 | public static boolean indexIsValid(int index, @NotNull final Collection collection) { 54 | int size = collection.size(); 55 | return index >= 0 && index < size; 56 | } 57 | 58 | @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") 59 | @Nullable 60 | public static String getFileText(@Nullable final String parentDir, @NotNull final String fileName, boolean wrapHTML, 61 | @NotNull final String encoding) { 62 | final File inputFile = parentDir != null ? new File(parentDir, fileName) : new File(fileName); 63 | if (!inputFile.exists()) return null; 64 | final StringBuilder stepText = new StringBuilder(); 65 | BufferedReader reader = null; 66 | try { 67 | reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), encoding)); 68 | String line; 69 | while ((line = reader.readLine()) != null) { 70 | stepText.append(line).append("\n"); 71 | if (wrapHTML) { 72 | stepText.append("
"); 73 | } 74 | } 75 | return wrapHTML ? UIUtil.toHtml(stepText.toString()) : stepText.toString(); 76 | } catch (IOException e) { 77 | LOG.info("Failed to get file text from file " + fileName, e); 78 | } finally { 79 | closeSilently(reader); 80 | } 81 | return null; 82 | } 83 | 84 | public static void updateAction(@NotNull final AnActionEvent e) { 85 | final Presentation presentation = e.getPresentation(); 86 | presentation.setEnabled(false); 87 | final Project project = e.getProject(); 88 | if (project != null) { 89 | final StudyEditor studyEditor = getSelectedStudyEditor(project); 90 | if (studyEditor != null) { 91 | presentation.setEnabledAndVisible(true); 92 | } 93 | } 94 | } 95 | 96 | public static void updateToolWindows(@NotNull final Project project) { 97 | update(project); 98 | 99 | // final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); 100 | // createProgressToolWindowContent(project, windowManager); 101 | } 102 | 103 | public static void initToolWindows(@NotNull final Project project) { 104 | final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); 105 | windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager().removeAllContents(false); 106 | StudyToolWindowFactory factory = new StudyToolWindowFactory(); 107 | factory.createToolWindowContent(project, windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW)); 108 | 109 | // createProgressToolWindowContent(project, windowManager); 110 | } 111 | 112 | private static void createProgressToolWindowContent(@NotNull Project project, ToolWindowManager windowManager) { 113 | // windowManager.getToolWindow(StudyProgressToolWindowFactory.ID).getContentManager().removeAllContents(false); 114 | // StudyProgressToolWindowFactory windowFactory = new StudyProgressToolWindowFactory(); 115 | // windowFactory.createToolWindowContent(project, windowManager.getToolWindow(StudyProgressToolWindowFactory.ID)); 116 | } 117 | 118 | @Nullable 119 | public static StudyToolWindow getStudyToolWindow(@NotNull final Project project) { 120 | ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); 121 | if (toolWindow != null) { 122 | Content[] contents = toolWindow.getContentManager().getContents(); 123 | for (Content content : contents) { 124 | JComponent component = content.getComponent(); 125 | if (component != null && component instanceof StudyToolWindow) { 126 | return (StudyToolWindow) component; 127 | } 128 | } 129 | } 130 | return null; 131 | } 132 | 133 | public static void deleteFile(@NotNull final VirtualFile file) { 134 | try { 135 | file.delete(StudyUtils.class); 136 | } catch (IOException e) { 137 | LOG.error(e); 138 | } 139 | } 140 | 141 | public static void showCheckPopUp(@NotNull final Project project, @NotNull final Balloon balloon) { 142 | final StudyEditor studyEditor = getSelectedStudyEditor(project); 143 | assert studyEditor != null; 144 | 145 | balloon.show(computeLocation(studyEditor.getEditor()), Balloon.Position.above); 146 | Disposer.register(project, balloon); 147 | } 148 | 149 | public static RelativePoint computeLocation(Editor editor) { 150 | 151 | final Rectangle visibleRect = editor.getComponent().getVisibleRect(); 152 | Point point = new Point(visibleRect.x + visibleRect.width + 10, 153 | visibleRect.y + 10); 154 | return new RelativePoint(editor.getComponent(), point); 155 | } 156 | 157 | 158 | @Nullable 159 | public static StudyEditor getSelectedStudyEditor(@NotNull final Project project) { 160 | try { 161 | final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow(). 162 | getSelectedEditor().getSelectedEditorWithProvider().getFirst(); 163 | if (fileEditor instanceof StudyEditor) { 164 | return (StudyEditor) fileEditor; 165 | } 166 | } catch (Exception e) { 167 | return null; 168 | } 169 | return null; 170 | } 171 | 172 | @Nullable 173 | public static Editor getSelectedEditor(@NotNull final Project project) { 174 | final StudyEditor studyEditor = getSelectedStudyEditor(project); 175 | if (studyEditor != null) { 176 | return studyEditor.getEditor(); 177 | } 178 | return null; 179 | } 180 | 181 | @Nullable 182 | public static String getStepTextFromStep(VirtualFile file, Project project) { 183 | return CourseDefinitionStorage.getInstance(project).getText(file.getPath()); 184 | } 185 | 186 | @Nullable 187 | public static StudyPluginConfigurator getConfigurator(@NotNull final Project project) { 188 | StudyPluginConfigurator[] extensions = StudyPluginConfigurator.EP_NAME.getExtensions(); 189 | for (StudyPluginConfigurator extension : extensions) { 190 | if (extension.accept(project)) { 191 | return extension; 192 | } 193 | } 194 | return null; 195 | } 196 | 197 | public static String getStepText(@NotNull final Project project) { 198 | VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); 199 | // TaskFile taskFile = null; 200 | CourseDefinitionStorage ps = CourseDefinitionStorage.getInstance(project); 201 | String text = null; 202 | for (VirtualFile file : files) { 203 | text = ps.getText(file.getPath()); 204 | if (text != null && !text.isEmpty()) { 205 | break; 206 | } 207 | } 208 | if (text == null) { 209 | return EMPTY_STEP_TEXT; 210 | } 211 | return text; 212 | } 213 | 214 | public static void update(Project project) { 215 | final StudyToolWindow studyToolWindow = getStudyToolWindow(project); 216 | if (studyToolWindow != null) { 217 | String stepText = getStepText(project); 218 | studyToolWindow.setStepText(stepText); 219 | } 220 | } 221 | 222 | public static boolean hasJavaFx() { 223 | try { 224 | Class.forName("javafx.application.Platform"); 225 | return true; 226 | } catch (ClassNotFoundException e) { 227 | return false; 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/utils/MyLogger.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.utils; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | 5 | public class MyLogger { 6 | private Logger LOG; 7 | private static MyLogger ourInstance = new MyLogger(); 8 | 9 | public static MyLogger getInstance() { 10 | return ourInstance; 11 | } 12 | 13 | private MyLogger() { 14 | LOG = Logger.getInstance(MyLogger.class); 15 | } 16 | 17 | public Logger getLOG() { 18 | return LOG; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/utils/NotificationTemplates.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.utils; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationType; 5 | 6 | public class NotificationTemplates { 7 | public static final Notification CONNECTION_ERROR = 8 | new Notification("Connection.error", "Connection error", "Please check your internet configuration.", NotificationType.ERROR); 9 | 10 | public static final Notification CERTIFICATE_ERROR = 11 | new Notification("Certificate.error", "Certificate error", "Please check your internet configuration.", NotificationType.ERROR); 12 | 13 | public static final Notification DOWNLOAD_WARNING = 14 | new Notification("Step.download", "Download error", "You didn't send a Step", NotificationType.WARNING); 15 | } 16 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/utils/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.utils; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.openapi.project.Project; 5 | 6 | public class NotificationUtils { 7 | private NotificationUtils() { 8 | } 9 | 10 | public static void initRuntimeException(Notification notification, Project project) { 11 | notification.notify(project); 12 | throw new RuntimeException(); 13 | } 14 | 15 | public static void initRuntimeException(Project project) { 16 | initRuntimeException(NotificationTemplates.CONNECTION_ERROR, project); 17 | } 18 | 19 | public static void initRuntimeException(Project project, String text) { 20 | NotificationTemplates.CONNECTION_ERROR.notify(project); 21 | throw new RuntimeException(text); 22 | } 23 | 24 | public static void showNotification(Notification notification, Project project) { 25 | notification.notify(project); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.utils; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.LocalFileSystem; 5 | import org.stepik.plugin.storages.CourseDefinitionStorage; 6 | 7 | import java.util.HashSet; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | public class Utils { 12 | public static void refreshFiles(Project project) { 13 | CourseDefinitionStorage projectService = CourseDefinitionStorage.getInstance(project); 14 | 15 | LocalFileSystem lfs = LocalFileSystem.getInstance(); 16 | Set set = projectService.getMapPathInfo().keySet(); 17 | Set removed = new HashSet<>(); 18 | set.forEach(x -> { 19 | if (lfs.findFileByPath(x) == null) 20 | removed.add(x); 21 | }); 22 | 23 | projectService.removeAll(removed); 24 | set.removeAll(removed); 25 | } 26 | 27 | public static String normalize(String text) { 28 | String[] words = text.split("\\W+"); 29 | StringBuilder sb = new StringBuilder(); 30 | for (int i = 0; i < words.length - 1; i++) { 31 | sb.append(words[i] + "_"); 32 | } 33 | if (words.length > 0) 34 | sb.append(words[words.length - 1]); 35 | return sb.toString(); 36 | } 37 | 38 | public static String getIdQuery(List list) { 39 | StringBuilder sb = new StringBuilder(); 40 | sb.append("?"); 41 | for (Integer id : list) { 42 | sb.append("ids[]=" + id + "&"); 43 | } 44 | return sb.toString(); 45 | } 46 | 47 | public static String parseUrl(String url) { 48 | if (url == null || url.isEmpty()) return ""; 49 | url = url.trim(); 50 | if (Character.isDigit(url.charAt(0))) { 51 | return url; 52 | } else { 53 | String[] path = url.split("/"); 54 | if (path[3].equals("course")) { 55 | String tmp[] = path[4].split("-"); 56 | return tmp[tmp.length - 1]; 57 | } 58 | } 59 | return ""; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/org/stepik/plugin/utils/YaTranslator.java: -------------------------------------------------------------------------------- 1 | package org.stepik.plugin.utils; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.mashape.unirest.http.HttpResponse; 5 | import com.mashape.unirest.http.JsonNode; 6 | import com.mashape.unirest.http.Unirest; 7 | import com.mashape.unirest.http.exceptions.UnirestException; 8 | import org.json.JSONArray; 9 | import org.stepik.plugin.storages.CourseDefinitionStorage; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class YaTranslator { 15 | final static private String API_URL = "https://translate.yandex.net/api/v1.5/tr.json/translate"; 16 | final static private String APP_KEY = "trnsl.1.1.20160514T085113Z.7ee4b2a3baf18163.933b409b8a7ee466edcb2682f2cec46eadb65f5e"; 17 | final static private String TEXT = "text"; 18 | final static private String KEY = "key"; 19 | final static private String LANG = "lang"; 20 | final static private String EN_RU = "en-ru"; 21 | final static private String RU_EN = "ru-en"; 22 | 23 | public static String translateRuToEn(String text) { 24 | HttpResponse response = null; 25 | try { 26 | response = Unirest 27 | .post(API_URL) 28 | .field(KEY, APP_KEY) 29 | .field(LANG, RU_EN) 30 | .field(TEXT, text) 31 | .asJson(); 32 | } catch (UnirestException e) { 33 | e.printStackTrace(); 34 | } 35 | 36 | return (String) response.getBody().getObject().getJSONArray(TEXT).get(0); 37 | } 38 | 39 | public static JSONArray translateRuToEn(List list) { 40 | HttpResponse response = null; 41 | try { 42 | response = Unirest 43 | .post(API_URL) 44 | .field(KEY, APP_KEY) 45 | .field(LANG, RU_EN) 46 | .field(TEXT, list) 47 | .asJson(); 48 | } catch (UnirestException e) { 49 | e.printStackTrace(); 50 | } 51 | 52 | return response.getBody().getObject().getJSONArray(TEXT); 53 | } 54 | 55 | public static List translateNames(List names, String level, Project project) { 56 | CourseDefinitionStorage ws = CourseDefinitionStorage.getInstance(project); 57 | List ans = new ArrayList<>(); 58 | if (ws.isTranslate()) { 59 | JSONArray arr = YaTranslator.translateRuToEn(names); 60 | for (int i = 0; i < names.size(); i++) { 61 | ans.add("_" + (i + 1) + "_" + Utils.normalize(arr.getString(i))); 62 | } 63 | } else { 64 | for (int i = 0; i < names.size(); i++) { 65 | ans.add(level + (i + 1)); 66 | } 67 | } 68 | return ans; 69 | } 70 | } 71 | --------------------------------------------------------------------------------