├── .idea ├── .name ├── .gitignore ├── kotlinc.xml ├── vcs.xml ├── misc.xml ├── gradle.xml └── uiDesigner.xml ├── settings.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── java │ └── com │ │ └── videogameaholic │ │ └── intellij │ │ └── starcoder │ │ ├── settings │ │ ├── TabActionOption.java │ │ ├── SettingsPanel.java │ │ ├── StarCoderSettingsProvider.java │ │ ├── StarCoderSettings.java │ │ └── SettingsPanel.form │ │ ├── StarCoderIcons.java │ │ ├── StarCoderWidgetFactory.java │ │ ├── StarCoderStatus.java │ │ ├── action │ │ ├── CodeGenTabAction.java │ │ ├── CodeGenPromptAction.java │ │ ├── CodeGenInsertLineAction.java │ │ └── CodeGenInsertAllAction.java │ │ ├── PromptModel.java │ │ ├── StarCoderService.java │ │ ├── CodeGenHintRenderer.kt │ │ └── StarCoderWidget.java │ └── resources │ ├── META-INF │ ├── plugin.xml │ └── pluginIcon.svg │ └── icons │ ├── widgetDisabled.svg │ ├── widgetDisabled_dark.svg │ ├── actionIcon.svg │ ├── widgetEnabled.svg │ ├── widgetError.svg │ ├── actionIcon_dark.svg │ └── widgetEnabled_dark.svg ├── .gitignore ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /.idea/.name: -------------------------------------------------------------------------------- 1 | starcoder-intellij -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "starcoder-intellij" -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/videogameaholic/starcoder-intellij/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/settings/TabActionOption.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder.settings; 2 | 3 | public enum TabActionOption { 4 | // TODO add action class here? 5 | ALL("All suggestions"), 6 | SINGLE("Single line at a time"), 7 | DISABLED("None"); 8 | 9 | private String description; 10 | 11 | TabActionOption(String description) { 12 | this.description = description; 13 | } 14 | 15 | @Override public String toString() { return description; } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/StarCoderIcons.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | public interface StarCoderIcons { 8 | Icon Action = IconLoader.getIcon("/icons/actionIcon.svg", StarCoderIcons.class); 9 | Icon WidgetEnabled = IconLoader.getIcon("/icons/widgetEnabled.svg", StarCoderIcons.class); 10 | Icon WidgetDisabled = IconLoader.getIcon("/icons/widgetDisabled.svg", StarCoderIcons.class); 11 | Icon WidgetError = IconLoader.getIcon("/icons/widgetError.svg", StarCoderIcons.class); 12 | } 13 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | starcoder-intellij 2 | ====================== 3 | 4 | IntelliJ plugin for StarCoder AI code completion via Hugging Face API. 5 | 6 | More information: https://huggingface.co/bigcode/starcoder 7 | ![](https://huggingface.co/datasets/bigcode/admin/resolve/main/StarCoderBanner.png) 8 | 9 | ## Features: 10 | 11 | * AI code completion suggestions as you type. 12 | * Prompt AI with selected text in the editor. 13 | * Modify API URL to switch between model endpoints. 14 | * Supports StarCoder, StarCoder2, SantaCoder, and Code Llama models. 15 | * Advanced parameters for model response adjustment. 16 | 17 | ## Getting Started: 18 | 19 | 1. Register on https://huggingface.co 20 | 2. Generate bearer token on https://huggingface.co/settings/tokens 21 | 3. Enter the token in Preferences -> Editor -> General -> StarCoder 22 | 4. Suggestions appear as you type if enabled, or right-click selected text to manually prompt. 23 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/StarCoderWidgetFactory.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.Disposer; 5 | import com.intellij.openapi.wm.StatusBarWidget; 6 | import com.intellij.openapi.wm.impl.status.widget.StatusBarEditorBasedWidgetFactory; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NonNls; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class StarCoderWidgetFactory extends StatusBarEditorBasedWidgetFactory { 12 | @Override 13 | public @NonNls @NotNull String getId() { 14 | return StarCoderWidget.ID; 15 | } 16 | 17 | @Override 18 | public @Nls @NotNull String getDisplayName() { 19 | return "StarCoder"; 20 | } 21 | 22 | @Override 23 | public @NotNull StatusBarWidget createWidget(@NotNull Project project) { 24 | return new StarCoderWidget(project); 25 | } 26 | 27 | @Override 28 | public void disposeWidget(@NotNull StatusBarWidget widget) { 29 | Disposer.dispose(widget); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/StarCoderStatus.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder; 2 | 3 | import java.util.stream.Stream; 4 | 5 | public enum StarCoderStatus { 6 | UNKNOWN(0,"Unknown"), 7 | OK(200,"OK"), 8 | BAD_REQUEST(400,"Bad request/token"), 9 | NOT_FOUND(404,"404 Not found"), 10 | TOO_MANY_REQUESTS(429,"Too many requests right now"), 11 | BAD_GATEWAY(502, "Bad gateway"), 12 | UNAVAILABLE(503,"Service unavailable"), 13 | PARAMS(422,"422 Unprocessable (Check Parameters)"); 14 | 15 | private int code; 16 | private String displayValue; 17 | 18 | StarCoderStatus(int i, String s) { 19 | code = i; 20 | displayValue = s; 21 | } 22 | 23 | public int getCode() { 24 | return code; 25 | } 26 | 27 | public String getDisplayValue() { 28 | return displayValue; 29 | } 30 | 31 | public static StarCoderStatus getStatusByCode(int code) { 32 | return Stream.of(StarCoderStatus.values()) 33 | .filter(s -> s.getCode() == code) 34 | .findFirst() 35 | .orElse(StarCoderStatus.UNKNOWN); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/action/CodeGenTabAction.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder.action; 2 | 3 | import com.intellij.openapi.actionSystem.CommonDataKeys; 4 | import com.intellij.openapi.actionSystem.DataContext; 5 | import com.intellij.openapi.editor.Caret; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.editor.actionSystem.EditorActionHandler; 8 | import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import com.videogameaholic.intellij.starcoder.settings.StarCoderSettings; 11 | import com.videogameaholic.intellij.starcoder.settings.TabActionOption; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | public class CodeGenTabAction extends EditorWriteActionHandler { 16 | protected final EditorActionHandler myOriginalHandler; 17 | 18 | public CodeGenTabAction(EditorActionHandler originalHandler) { 19 | myOriginalHandler = originalHandler; 20 | } 21 | 22 | @Override 23 | public void executeWriteAction(@NotNull Editor editor, @Nullable Caret caret, DataContext dataContext) { 24 | if(!insertCodeSuggestion(editor, caret, dataContext)) { 25 | myOriginalHandler.execute(editor, caret, dataContext); 26 | } 27 | } 28 | 29 | private boolean insertCodeSuggestion(Editor editor, Caret caret, DataContext dataContext) { 30 | VirtualFile file = dataContext.getData(CommonDataKeys.VIRTUAL_FILE); 31 | TabActionOption tabActionOption = StarCoderSettings.getInstance().getTabActionOption(); 32 | 33 | switch (tabActionOption) { 34 | case ALL: 35 | return CodeGenInsertAllAction.performAction(editor, caret, file); 36 | case SINGLE: 37 | return CodeGenInsertLineAction.performAction(editor, caret, file); 38 | default: 39 | return false; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/action/CodeGenPromptAction.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder.action; 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.application.ApplicationManager; 7 | import com.intellij.openapi.command.WriteCommandAction; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.videogameaholic.intellij.starcoder.StarCoderService; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class CodeGenPromptAction extends AnAction { 14 | 15 | @Override 16 | public void actionPerformed(@NotNull AnActionEvent e) { 17 | Editor focusedEditor =e.getDataContext().getData(CommonDataKeys.EDITOR); 18 | if(focusedEditor == null) return; 19 | 20 | int selectionStart = focusedEditor.getSelectionModel().getSelectionStart(); 21 | int selectionEnd = focusedEditor.getSelectionModel().getSelectionEnd(); 22 | String selectedText = focusedEditor.getCaretModel().getCurrentCaret().getSelectedText(); 23 | if(StringUtils.isEmpty(selectedText)) return; 24 | 25 | StarCoderService starCoder = ApplicationManager.getApplication().getService(StarCoderService.class); 26 | String replacementSuggestion = starCoder.replacementSuggestion(selectedText); 27 | WriteCommandAction.runWriteCommandAction(focusedEditor.getProject(),"StarCoder Insert", null, () -> 28 | focusedEditor.getDocument().replaceString(selectionStart, selectionEnd, replacementSuggestion)); 29 | } 30 | 31 | @Override 32 | public void update(@NotNull AnActionEvent e) { 33 | // Only show the action if there is selected text in the editor 34 | String selection = ""; 35 | Editor focusedEditor =e.getDataContext().getData(CommonDataKeys.EDITOR); 36 | if(focusedEditor != null) { 37 | selection = focusedEditor.getCaretModel().getCurrentCaret().getSelectedText(); 38 | } 39 | e.getPresentation().setEnabledAndVisible(!StringUtils.isEmpty(selection)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/action/CodeGenInsertLineAction.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder.action; 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.command.WriteCommandAction; 7 | import com.intellij.openapi.editor.Caret; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import com.videogameaholic.intellij.starcoder.StarCoderWidget; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class CodeGenInsertLineAction extends AnAction { 14 | @Override 15 | public void actionPerformed(@NotNull AnActionEvent e) { 16 | Editor editor = e.getData(CommonDataKeys.EDITOR); 17 | Caret caret = e.getData(CommonDataKeys.CARET); 18 | VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE); 19 | if(!performAction(editor, caret, file)) { 20 | // TODO log? 21 | } 22 | } 23 | 24 | @Override 25 | public void update(@NotNull AnActionEvent e) { 26 | // Only allow this if there are hints in the userdata. 27 | VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE); 28 | if (file == null) return; 29 | 30 | String[] hints = file.getUserData(StarCoderWidget.STAR_CODER_CODE_SUGGESTION); 31 | e.getPresentation().setEnabledAndVisible(hints != null && hints.length > 0); 32 | } 33 | 34 | public static boolean performAction(Editor editor, Caret caret, VirtualFile file) { 35 | if (file == null || editor == null) return false; 36 | 37 | String[] hints = file.getUserData(StarCoderWidget.STAR_CODER_CODE_SUGGESTION); 38 | if((hints == null) || (hints.length == 0)) return false; 39 | 40 | Integer starCoderPos = file.getUserData(StarCoderWidget.STAR_CODER_POSITION); 41 | int lastPosition = (starCoderPos==null) ? 0 : starCoderPos; 42 | if((caret == null) || (caret.getOffset() != lastPosition)) return false; 43 | 44 | final String insertText = hints[0]; 45 | WriteCommandAction.runWriteCommandAction(editor.getProject(), "StarCoder Insert", null, () -> { 46 | editor.getDocument().insertString(lastPosition, insertText); 47 | editor.getCaretModel().moveToOffset(lastPosition + insertText.length()); 48 | }); 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/action/CodeGenInsertAllAction.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder.action; 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.command.WriteCommandAction; 7 | import com.intellij.openapi.editor.Caret; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import com.videogameaholic.intellij.starcoder.StarCoderWidget; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.StringJoiner; 14 | 15 | public class CodeGenInsertAllAction extends AnAction { 16 | 17 | @Override 18 | public void actionPerformed(@NotNull AnActionEvent e) { 19 | Editor editor = e.getData(CommonDataKeys.EDITOR); 20 | Caret caret = e.getData(CommonDataKeys.CARET); 21 | VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE); 22 | if(!performAction(editor, caret, file)) { 23 | // TODO log? 24 | } 25 | } 26 | 27 | @Override 28 | public void update(@NotNull AnActionEvent e) { 29 | // Only allow this if there are hints in the userdata. 30 | VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE); 31 | if (file == null) return; 32 | 33 | String[] hints = file.getUserData(StarCoderWidget.STAR_CODER_CODE_SUGGESTION); 34 | e.getPresentation().setEnabledAndVisible(hints != null && hints.length > 0); 35 | } 36 | 37 | public static boolean performAction(Editor editor, Caret caret, VirtualFile file) { 38 | if (file == null) return false; 39 | 40 | String[] hints = file.getUserData(StarCoderWidget.STAR_CODER_CODE_SUGGESTION); 41 | if((hints == null) || (hints.length == 0)) return false; 42 | 43 | Integer starCoderPos = file.getUserData(StarCoderWidget.STAR_CODER_POSITION); 44 | int lastPosition = (starCoderPos==null) ? 0 : starCoderPos; 45 | if((caret == null) || (caret.getOffset() != lastPosition)) return false; 46 | 47 | StringJoiner insertTextJoiner = new StringJoiner(""); 48 | for (String hint : hints) { 49 | insertTextJoiner.add(hint); 50 | } 51 | 52 | file.putUserData(StarCoderWidget.STAR_CODER_CODE_SUGGESTION, null); 53 | 54 | String insertText = insertTextJoiner.toString(); 55 | WriteCommandAction.runWriteCommandAction(editor.getProject(), "StarCoder Insert", null, () -> { 56 | editor.getDocument().insertString(lastPosition, insertText); 57 | editor.getCaretModel().moveToOffset(lastPosition + insertText.length()); 58 | }); 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/PromptModel.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import javax.annotation.Nullable; 6 | import java.util.stream.Stream; 7 | 8 | public enum PromptModel { 9 | STARCODER ("starcoder","StarCoder", "","","", "<|endoftext|>"), 10 | SANTACODER ("santacoder","SantaCoder", "","","", "<|endoftext|>"), 11 | // Whitespace for Code Llama is intentional 12 | CODELLAMA ("codellama","Code Llama", "
 "," "," ", "");
13 | 
14 |     private final String id;
15 |     private final String displayName;
16 |     private final String prefixTag;
17 |     private final String suffixTag;
18 |     private final String middleTag;
19 |     private final String endTag;
20 | 
21 |     private PromptModel(String uniqueId, String name, String prefix, String suffix, String middle, String end)
22 |     {
23 |         id = uniqueId;
24 |         displayName = name;
25 |         prefixTag = prefix;
26 |         suffixTag = suffix;
27 |         middleTag = middle;
28 |         endTag = end;
29 |     }
30 | 
31 |     public String getId() {
32 |         return id;
33 |     }
34 | 
35 |     public String getDisplayName() {
36 |         return displayName;
37 |     }
38 | 
39 |     public String toString() {
40 |         return displayName;
41 |     }
42 | 
43 |     public static PromptModel fromId(String attributeValue) {
44 |         return Stream.of(PromptModel.values())
45 |                 .filter(model -> model.getId().equals(attributeValue))
46 |                 .findFirst()
47 |                 .orElse(PromptModel.STARCODER);
48 |     }
49 | 
50 |     public String generateFIMPrompt(String metaData, String code, int fillPosition) {
51 |         // First validate text does not already contain tokens that would confuse the AI.
52 |         // Future: May replace with alternate tokens and switch back after response.
53 |         if(code.contains(prefixTag) || code.contains(suffixTag) || code.contains(middleTag) || code.contains(endTag)) return "";
54 | 
55 |         String prefix = code.substring(0, fillPosition);
56 |         String suffix = code.substring(fillPosition);
57 |         return metaData + prefixTag + prefix + suffixTag + suffix + middleTag;
58 |     }
59 | 
60 |     @Nullable
61 |     public String[] buildSuggestionList(String generatedText) {
62 |         String[] suggestionList = null;
63 |         generatedText = generatedText.replace(endTag, "");
64 |         if(generatedText.contains(middleTag)) {
65 |             String[] parts = generatedText.split(middleTag);
66 |             if(parts.length > 1) {
67 |                 suggestionList = StringUtils.splitPreserveAllTokens(parts[1], "\n");
68 |                 if(suggestionList.length == 1 && suggestionList[0].trim().length() == 0) return null;
69 |                 if(suggestionList.length > 1) {
70 |                     for (int i = 0; i < suggestionList.length; i++) {
71 |                         StringBuilder sb = new StringBuilder(suggestionList[i]);
72 |                         sb.append("\n");
73 |                         suggestionList[i] = sb.toString();
74 |                     }
75 |                 }
76 |             }
77 |         }
78 |         return suggestionList;
79 |     }
80 | }
81 | 


--------------------------------------------------------------------------------
/src/main/java/com/videogameaholic/intellij/starcoder/settings/SettingsPanel.java:
--------------------------------------------------------------------------------
  1 | package com.videogameaholic.intellij.starcoder.settings;
  2 | 
  3 | import com.intellij.ui.EnumComboBoxModel;
  4 | import com.videogameaholic.intellij.starcoder.PromptModel;
  5 | 
  6 | import javax.swing.*;
  7 | 
  8 | public class SettingsPanel {
  9 | 
 10 |     private JPanel panel;
 11 |     private JTextField apiUrlTextField;
 12 |     private JPanel API;
 13 |     private JPanel Parameters;
 14 |     private JTextField bearerTokenTextField;
 15 |     private JTextField temperatureTextField;
 16 |     private JTextField maxNewTokensTextField;
 17 |     private JTextField topPTextField;
 18 |     private JTextField repetitionTextField;
 19 |     private JCheckBox enableSAYTCheckBox;
 20 |     private JPanel Settings;
 21 |     private JPanel ParamOuter;
 22 |     private JPanel TabActionPanel;
 23 |     private JComboBox tabActionComboBox;
 24 |     private JLabel tabActionLabel;
 25 |     private JPanel FIMTokenPanel;
 26 |     private JLabel fimModelLabel;
 27 |     private JComboBox fimModelComboBox;
 28 |     private JTextField maxContextLinesTextField;
 29 | 
 30 |     public SettingsPanel() {
 31 |         tabActionComboBox.setModel(new EnumComboBoxModel<>(TabActionOption.class));
 32 |         fimModelComboBox.setModel(new EnumComboBoxModel<>(PromptModel.class));
 33 |         enableSAYTCheckBox.addActionListener(e -> {
 34 |             tabActionLabel.setEnabled(enableSAYTCheckBox.isSelected());
 35 |             tabActionComboBox.setEnabled(enableSAYTCheckBox.isSelected());
 36 |             fimModelLabel.setEnabled(enableSAYTCheckBox.isSelected());
 37 |         	fimModelComboBox.setEnabled(enableSAYTCheckBox.isSelected());
 38 |         });
 39 |     }
 40 | 
 41 |     public JComponent getPanel() {
 42 |         return panel;
 43 |     }
 44 | 
 45 |     public String getApiUrl() {
 46 |         return apiUrlTextField.getText();
 47 |     }
 48 | 
 49 |     public void setApiUrl(String apiUrl) {
 50 |         apiUrlTextField.setText(apiUrl);
 51 |     }
 52 | 
 53 |     public String getApiToken() {
 54 |         return bearerTokenTextField.getText();
 55 |     }
 56 | 
 57 |     public void setApiToken(String bearerToken) {
 58 |         bearerTokenTextField.setText(bearerToken);
 59 |     }
 60 | 
 61 |     public String getTemperature() {
 62 |         return temperatureTextField.getText();
 63 |     }
 64 | 
 65 |     public void setTemperature(String temperature) {
 66 |         temperatureTextField.setText(temperature);
 67 |     }
 68 | 
 69 |     public String getMaxNewTokens() {
 70 |         return maxNewTokensTextField.getText();
 71 |     }
 72 | 
 73 |     public void setMaxNewTokens(String maxNewTokens) {
 74 |         maxNewTokensTextField.setText(maxNewTokens);
 75 |     }
 76 | 
 77 |     public String getTopP() {
 78 |         return topPTextField.getText();
 79 |     }
 80 | 
 81 |     public void setTopP(String topP) {
 82 |         topPTextField.setText(topP);
 83 |     }
 84 | 
 85 |     public String getRepetition() {
 86 |         return repetitionTextField.getText();
 87 |     }
 88 | 
 89 |     public void setRepetition(String repetition) {
 90 |         repetitionTextField.setText(repetition);
 91 |     }
 92 | 
 93 |     public String getMaxContextLines() {
 94 |         return maxContextLinesTextField.getText();
 95 |     }
 96 | 
 97 |     public void setMaxContextLines(String maxLines) {
 98 |         maxContextLinesTextField.setText(maxLines);
 99 |     }
100 | 
101 |     public boolean getEnableSAYTCheckBox() {
102 |         return enableSAYTCheckBox.isSelected();
103 |     }
104 | 
105 |     public void setEnableSAYTCheckBox(boolean enableSAYT) {
106 |         enableSAYTCheckBox.setSelected(enableSAYT);
107 |         tabActionLabel.setEnabled(enableSAYT);
108 |         tabActionComboBox.setEnabled(enableSAYT);
109 |         fimModelLabel.setEnabled(enableSAYT);
110 |         fimModelComboBox.setEnabled(enableSAYT);
111 |     }
112 | 
113 |     public TabActionOption getTabActionOption() {
114 |         return (TabActionOption) tabActionComboBox.getModel().getSelectedItem();
115 |     }
116 | 
117 |     public void setTabActionOption(TabActionOption option) {
118 |         tabActionComboBox.getModel().setSelectedItem(option);
119 |     }
120 | 
121 |     public PromptModel getFimTokenModel() {
122 |     	return (PromptModel) fimModelComboBox.getSelectedItem();
123 |     }
124 | 
125 |     public void setFimTokenModel(PromptModel model) {
126 | 		fimModelComboBox.setSelectedItem(model);
127 |     }
128 | 
129 | }
130 | 


--------------------------------------------------------------------------------
/src/main/java/com/videogameaholic/intellij/starcoder/settings/StarCoderSettingsProvider.java:
--------------------------------------------------------------------------------
  1 | package com.videogameaholic.intellij.starcoder.settings;
  2 | 
  3 | import com.intellij.application.options.editor.EditorOptionsProvider;
  4 | import com.intellij.notification.Notification;
  5 | import com.intellij.notification.NotificationType;
  6 | import com.intellij.notification.Notifications;
  7 | import com.intellij.openapi.options.ConfigurationException;
  8 | import com.intellij.openapi.project.Project;
  9 | import com.intellij.openapi.project.ProjectManager;
 10 | import com.intellij.openapi.util.NlsContexts;
 11 | import com.intellij.openapi.wm.WindowManager;
 12 | import com.videogameaholic.intellij.starcoder.StarCoderWidget;
 13 | import org.jetbrains.annotations.NonNls;
 14 | import org.jetbrains.annotations.NotNull;
 15 | import org.jetbrains.annotations.Nullable;
 16 | 
 17 | import javax.swing.*;
 18 | import java.awt.*;
 19 | 
 20 | public class StarCoderSettingsProvider implements EditorOptionsProvider {
 21 |     private SettingsPanel settingsPanel;
 22 | 
 23 |     @Override
 24 |     public @NotNull @NonNls String getId() {
 25 |         return "StarCoder.Settings";
 26 |     }
 27 | 
 28 |     @Override
 29 |     public @NlsContexts.ConfigurableName String getDisplayName() {
 30 |         return "StarCoder";
 31 |     }
 32 | 
 33 |     @Override
 34 |     public @Nullable JComponent createComponent() {
 35 |         if(settingsPanel == null) {
 36 |             settingsPanel = new SettingsPanel();
 37 |         }
 38 |         return settingsPanel.getPanel();
 39 |     }
 40 | 
 41 |     @Override
 42 |     public boolean isModified() {
 43 |         StarCoderSettings savedSettings = StarCoderSettings.getInstance();
 44 | 
 45 |         return !savedSettings.getApiURL().equals(settingsPanel.getApiUrl())
 46 |                 || !savedSettings.getApiToken().equals(settingsPanel.getApiToken())
 47 |                 || savedSettings.getTabActionOption() != settingsPanel.getTabActionOption()
 48 |                 || savedSettings.isSaytEnabled() != settingsPanel.getEnableSAYTCheckBox()
 49 |                 || savedSettings.getTemperature() != Float.parseFloat(settingsPanel.getTemperature())
 50 |                 || savedSettings.getMaxNewTokens() != Integer.parseInt(settingsPanel.getMaxNewTokens())
 51 |                 || savedSettings.getTopP() != Float.parseFloat(settingsPanel.getTopP())
 52 |                 || savedSettings.getRepetitionPenalty() != Float.parseFloat(settingsPanel.getRepetition())
 53 |                 || savedSettings.getMaxContextLines() != Integer.parseInt(settingsPanel.getMaxContextLines())
 54 |                 ||!savedSettings.getFimTokenModel().equals(settingsPanel.getFimTokenModel());
 55 |     }
 56 | 
 57 |     @Override
 58 |     public void apply() throws ConfigurationException {
 59 |         StarCoderSettings savedSettings = StarCoderSettings.getInstance();
 60 | 
 61 |         savedSettings.setApiURL(settingsPanel.getApiUrl());
 62 |         savedSettings.setApiToken(settingsPanel.getApiToken());
 63 |         savedSettings.setSaytEnabled(settingsPanel.getEnableSAYTCheckBox());
 64 |         savedSettings.setTabActionOption(settingsPanel.getTabActionOption());
 65 |         savedSettings.setTemperature(settingsPanel.getTemperature());
 66 |         savedSettings.setMaxNewTokens(settingsPanel.getMaxNewTokens());
 67 |         savedSettings.setTopP(settingsPanel.getTopP());
 68 |         savedSettings.setRepetitionPenalty(settingsPanel.getRepetition());
 69 |         savedSettings.setMaxContextLines(settingsPanel.getMaxContextLines());
 70 |         savedSettings.setFimTokenModel(settingsPanel.getFimTokenModel());
 71 |         if(settingsPanel.getApiToken().isBlank()){
 72 |             Project[] projects = ProjectManager.getInstance().getOpenProjects();
 73 |             Project activeProject = null;
 74 |             for (Project project : projects) {
 75 |                 Window window = WindowManager.getInstance().suggestParentWindow(project);
 76 |                 if (window != null && window.isActive()) {
 77 |                     activeProject = project;
 78 |                 }
 79 |             }
 80 |             Notifications.Bus.notify(
 81 |                     new Notification("StarCoder","StarCoder", "StarCoder API token is recommended.", NotificationType.WARNING)
 82 |                     ,activeProject);
 83 |         }
 84 | 
 85 |         // Update the widget
 86 |         for (Project openProject : ProjectManager.getInstance().getOpenProjects()) {
 87 |             WindowManager.getInstance().getStatusBar(openProject).updateWidget(StarCoderWidget.ID);
 88 |         }
 89 |     }
 90 | 
 91 |     @Override
 92 |     public void reset() {
 93 |         StarCoderSettings savedSettings = StarCoderSettings.getInstance();
 94 | 
 95 |         settingsPanel.setApiUrl(savedSettings.getApiURL());
 96 |         settingsPanel.setApiToken(savedSettings.getApiToken());
 97 |         settingsPanel.setEnableSAYTCheckBox(savedSettings.isSaytEnabled());
 98 |         settingsPanel.setTabActionOption(savedSettings.getTabActionOption());
 99 |         settingsPanel.setTemperature(String.valueOf(savedSettings.getTemperature()));
100 |         settingsPanel.setMaxNewTokens(String.valueOf(savedSettings.getMaxNewTokens()));
101 |         settingsPanel.setTopP(String.valueOf(savedSettings.getTopP()));
102 |         settingsPanel.setRepetition(String.valueOf(savedSettings.getRepetitionPenalty()));
103 |         settingsPanel.setMaxContextLines(String.valueOf(savedSettings.getMaxContextLines()));
104 |         settingsPanel.setFimTokenModel(savedSettings.getFimTokenModel());
105 |     }
106 | }
107 | 


--------------------------------------------------------------------------------
/src/main/java/com/videogameaholic/intellij/starcoder/StarCoderService.java:
--------------------------------------------------------------------------------
  1 | package com.videogameaholic.intellij.starcoder;
  2 | 
  3 | import com.google.gson.Gson;
  4 | import com.google.gson.JsonArray;
  5 | import com.google.gson.JsonObject;
  6 | import com.google.gson.JsonSyntaxException;
  7 | import com.intellij.openapi.project.Project;
  8 | import com.intellij.openapi.project.ProjectManager;
  9 | import com.intellij.openapi.wm.WindowManager;
 10 | import com.videogameaholic.intellij.starcoder.settings.StarCoderSettings;
 11 | import org.apache.commons.lang3.StringUtils;
 12 | import org.apache.http.HttpHeaders;
 13 | import org.apache.http.HttpResponse;
 14 | import org.apache.http.client.methods.HttpPost;
 15 | import org.apache.http.entity.ContentType;
 16 | import org.apache.http.entity.StringEntity;
 17 | import org.apache.http.impl.client.CloseableHttpClient;
 18 | import org.apache.http.impl.client.HttpClients;
 19 | import org.apache.http.util.EntityUtils;
 20 | 
 21 | import java.io.IOException;
 22 | import java.util.Optional;
 23 | 
 24 | public class StarCoderService {
 25 | 
 26 |     private int statusCode = 200;
 27 | 
 28 |     public String[] getCodeCompletionHints(CharSequence editorContents, int cursorPosition) {
 29 |         StarCoderSettings settings = StarCoderSettings.getInstance();
 30 |         if(!settings.isSaytEnabled()) return null;
 31 | 
 32 |         PromptModel fimModel = settings.getFimTokenModel();
 33 |         String starCoderPrompt = fimModel.generateFIMPrompt("",editorContents.toString(),cursorPosition);
 34 |         if(starCoderPrompt.isEmpty()) return null;
 35 | 
 36 |         HttpPost httpPost = buildApiPost(settings, starCoderPrompt);
 37 |         String generatedText = getApiResponse(httpPost);
 38 |         return fimModel.buildSuggestionList(generatedText);
 39 |     }
 40 | 
 41 |     private HttpPost buildApiPost (StarCoderSettings settings, String starCoderPrompt) {
 42 |         String apiURL = settings.getApiURL();
 43 |         String bearerToken = settings.getApiToken();
 44 |         float temperature = settings.getTemperature();
 45 |         int maxNewTokens = settings.getMaxNewTokens();
 46 |         float topP = settings.getTopP();
 47 |         float repetitionPenalty = settings.getRepetitionPenalty();
 48 | 
 49 |         HttpPost httpPost = new HttpPost(apiURL);
 50 |         if(!bearerToken.isBlank()) httpPost.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + bearerToken);
 51 |         JsonObject httpBody = new JsonObject();
 52 |         httpBody.addProperty("inputs", starCoderPrompt);
 53 | 
 54 |         JsonObject parameters = new JsonObject();
 55 |         parameters.addProperty("temperature", temperature);
 56 |         parameters.addProperty("max_new_tokens", maxNewTokens);
 57 |         parameters.addProperty("top_p", topP);
 58 |         parameters.addProperty("repetition_penalty", repetitionPenalty);
 59 |         httpBody.add("parameters", parameters);
 60 | 
 61 |         StringEntity requestEntity = new StringEntity(httpBody.toString(), ContentType.APPLICATION_JSON);
 62 |         httpPost.setEntity(requestEntity);
 63 |         return httpPost;
 64 |     }
 65 | 
 66 |     private String getApiResponse(HttpPost httpPost) {
 67 |         String responseText = "";
 68 |         try {
 69 |             CloseableHttpClient httpClient = HttpClients.createDefault();
 70 |             HttpResponse response = httpClient.execute(httpPost);
 71 | 
 72 |             // Check the response status code
 73 |             int oldStatusCode = statusCode;
 74 |             statusCode = response.getStatusLine().getStatusCode();
 75 |             if (statusCode != oldStatusCode) {
 76 |                 // Update the widget based on the new status code
 77 |                 for (Project openProject : ProjectManager.getInstance().getOpenProjects()) {
 78 |                     WindowManager.getInstance().getStatusBar(openProject).updateWidget(StarCoderWidget.ID);
 79 |                 }
 80 |             }
 81 |             if (statusCode != 200) {
 82 |                 return responseText;
 83 |             }
 84 |             String responseBody = EntityUtils.toString(response.getEntity());
 85 |             JsonObject responseObject = parseResponse(responseBody);
 86 |             responseText = extractGeneratedText(responseObject).orElse("");
 87 | 
 88 |             httpClient.close();
 89 | 
 90 |         } catch (IOException e) {
 91 |             // TODO log exception
 92 |         }
 93 |         return responseText;
 94 |     }
 95 | 
 96 |     private JsonObject parseResponse(String responseBody) throws IOException {
 97 |         Gson gson = new Gson();
 98 |         JsonArray responseArray;
 99 | 
100 |         try {
101 |             responseArray = gson.fromJson(responseBody, JsonArray.class);
102 |             if (responseArray.size() > 0) {
103 |                 return responseArray.get(0).getAsJsonObject();
104 |             }
105 |         } catch (JsonSyntaxException ignored) {
106 |             // Fallback.  Response may be an object rather than an array.
107 |             return gson.fromJson(responseBody, JsonObject.class);
108 |         }
109 | 
110 |         throw new IllegalArgumentException("Response has empty body array");
111 |     }
112 | 
113 |     private Optional extractGeneratedText(JsonObject responseObject) {
114 |         if (responseObject.get("generated_text") != null) {
115 |             return Optional.of(responseObject.get("generated_text").getAsString());
116 |         }
117 |         throw new IllegalArgumentException("Invalid response body missing \\'generated_text\\' key");
118 |     }
119 | 
120 |     public String replacementSuggestion (String prompt) {
121 |         // Default to returning the same text.
122 |         String replacement = prompt;
123 | 
124 |         StarCoderSettings settings = StarCoderSettings.getInstance();
125 |         HttpPost httpPost = buildApiPost(settings, prompt);
126 |         String generatedText = getApiResponse(httpPost);
127 |         if(!StringUtils.isEmpty(generatedText)) {
128 |             replacement = generatedText;
129 |         }
130 | 
131 |         return replacement;
132 |     }
133 | 
134 |     public int getStatus () {
135 |         return statusCode;
136 |     }
137 | }
138 | 


--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 |     
  4 |     com.videogameaholic.intellij.starcoder
  5 | 
  6 |     
  8 |     StarCoder
  9 | 
 10 |     
 11 |     videogameaholic
 12 | 
 13 |     
 16 |     
 19 |     More information: https://huggingface.co/bigcode/starcoder
 20 |     

21 |

Features:
22 |

    23 |
  • AI code completion suggestions as you type.
  • 24 |
  • Prompt AI with selected text in the editor.
  • 25 |
  • Modify API URL to switch between model endpoints.
  • 26 |
  • Supports StarCoder, StarCoder2, SantaCoder, and Code Llama models.
  • 27 |
  • Advanced parameters for model response adjustment.
  • 28 |
29 |

30 |
31 |

Getting Started:
32 |

    33 |
  1. Register on https://huggingface.co
  2. 34 |
  3. Generate bearer token on https://huggingface.co/settings/tokens
  4. 35 |
  5. Enter the token in Preferences -> Editor -> General -> StarCoder
  6. 36 |
  7. Suggestions appear as you type if enabled, or right-click selected text to manually prompt.
  8. 37 |
      38 |

      39 | ]]> 40 | 41 | 1.0

      43 |
        44 |
      • Added setting to define number of lines to use as context.
      • 45 |
      • Added user-friendly name for http error code 422 which is typically a parameter error.
      • 46 |
      47 |

      0.230912

      48 |
        49 |
      • Better response handling for custom endpoints.
      • 50 |
      51 |

      0.230905

      52 |
        53 |
      • Added support for SantaCoder and Code Llama models.
      • 54 |
      • Added setting to switch between FIM models.
      • 55 |
      56 |

      0.230829

      57 |
        58 |
      • API token now optional, but recommended.
      • 59 |
      • Refactored hint renderer.
      • 60 |
      61 |

      0.230711

      62 |
        63 |
      • Added a delayed queue to reduce API call frequency.
      • 64 |
      65 |

      0.230703

      66 |
        67 |
      • Added insert single line action (hotkey Alt+S).
      • 68 |
      • Added hotkey for insert all suggestions action (Alt+A).
      • 69 |
      • Changed prompt hotkey to Alt+P
      • 70 |
      • Added setting to choose action called when pressing Tab.
      • 71 |
      • Added icons for widget.
      • 72 |
      • Widget icon turns red when service encounters error.
      • 73 |
      74 |

      0.230627

      75 |
        76 |
      • Added manual prompt through right-click > StarCoder Prompt
      • 77 |
      78 |

      0.230620

      79 |
        80 |
      • This is the initial release of the plugin.
      • 81 |
      82 | ]]> 83 |
      84 | 85 | 86 | 87 | 89 | com.intellij.modules.platform 90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | 117 | 120 | 121 | 122 | 123 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/settings/StarCoderSettings.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder.settings; 2 | 3 | import com.intellij.credentialStore.CredentialAttributes; 4 | import com.intellij.credentialStore.Credentials; 5 | import com.intellij.ide.passwordSafe.PasswordSafe; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.openapi.components.PersistentStateComponent; 8 | import com.intellij.openapi.components.State; 9 | import com.intellij.openapi.components.Storage; 10 | import com.videogameaholic.intellij.starcoder.PromptModel; 11 | import org.jdom.Element; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | @State(name = "StarCoderSettings", storages = @Storage("starcoder_settings.xml")) 16 | public class StarCoderSettings implements PersistentStateComponent { 17 | public static final String SETTINGS_TAG = "StarCoderSettings"; 18 | private static final String API_URL_TAG = "API_URL"; 19 | private static final CredentialAttributes CREDENTIAL_ATTRIBUTES = new CredentialAttributes(StarCoderSettings.class.getName(), "STARCODER_BEARER_TOKEN"); 20 | private static final String SAYT_TAG = "SAYT_ENABLED"; 21 | private static final String TAB_ACTION_TAG = "TAB_ACTION"; 22 | private static final String TEMPERATURE_TAG = "TEMPERATURE"; 23 | private static final String MAX_NEW_TOKENS_TAG = "MAX_NEW_TOKENS"; 24 | private static final String TOP_P_TAG = "TOP_P"; 25 | private static final String REPEAT_PENALTY_TAG = "REPEAT_PENALTY"; 26 | private static final String FIM_MODEL_TAG = "FIM_TOKEN_MODEL"; 27 | private static final String MAX_CONTEXT_LINES_TAG = "MAX_CONTEXT_LINES"; 28 | 29 | private boolean saytEnabled = true; 30 | private String apiURL = "https://api-inference.huggingface.co/models/bigcode/starcoder"; 31 | private TabActionOption tabActionOption = TabActionOption.ALL; 32 | private float temperature = 0.2f; 33 | private int maxNewTokens = 256; 34 | private float topP = 0.9f; 35 | private float repetitionPenalty = 1.2f; 36 | private PromptModel fimTokenModel = PromptModel.STARCODER; 37 | private int maxContextLines = 800; 38 | 39 | private static final StarCoderSettings starCoderSettingsInstance = new StarCoderSettings(); 40 | 41 | @Override 42 | public @Nullable Element getState() { 43 | Element state = new Element(SETTINGS_TAG); 44 | state.setAttribute(API_URL_TAG, getApiURL()); 45 | state.setAttribute(SAYT_TAG, Boolean.toString(isSaytEnabled())); 46 | state.setAttribute(TAB_ACTION_TAG, getTabActionOption().name()); 47 | state.setAttribute(TEMPERATURE_TAG, String.valueOf(getTemperature())); 48 | state.setAttribute(MAX_NEW_TOKENS_TAG, String.valueOf(getMaxNewTokens())); 49 | state.setAttribute(TOP_P_TAG, String.valueOf(getTopP())); 50 | state.setAttribute(REPEAT_PENALTY_TAG, String.valueOf(getRepetitionPenalty())); 51 | state.setAttribute(MAX_CONTEXT_LINES_TAG, String.valueOf(getMaxContextLines())); 52 | state.setAttribute(FIM_MODEL_TAG, getFimTokenModel().getId()); 53 | return state; 54 | } 55 | 56 | @Override 57 | public void loadState(@NotNull Element state) { 58 | if(state.getAttributeValue(API_URL_TAG)!=null){ 59 | setApiURL(state.getAttributeValue(API_URL_TAG)); 60 | } 61 | if(state.getAttributeValue(SAYT_TAG)!=null){ 62 | setSaytEnabled(Boolean.parseBoolean(state.getAttributeValue(SAYT_TAG))); 63 | } 64 | if(state.getAttributeValue(TAB_ACTION_TAG)!=null){ 65 | setTabActionOption(TabActionOption.valueOf(state.getAttributeValue(TAB_ACTION_TAG))); 66 | } 67 | if(state.getAttributeValue(TEMPERATURE_TAG)!=null){ 68 | setTemperature(state.getAttributeValue(TEMPERATURE_TAG)); 69 | } 70 | if(state.getAttributeValue(MAX_NEW_TOKENS_TAG)!=null){ 71 | setMaxNewTokens(state.getAttributeValue(MAX_NEW_TOKENS_TAG)); 72 | } 73 | if(state.getAttributeValue(TOP_P_TAG)!=null){ 74 | setTopP(state.getAttributeValue(TOP_P_TAG)); 75 | } 76 | if(state.getAttributeValue(REPEAT_PENALTY_TAG)!=null){ 77 | setRepetitionPenalty(state.getAttributeValue(REPEAT_PENALTY_TAG)); 78 | } 79 | if(state.getAttributeValue(MAX_CONTEXT_LINES_TAG)!=null){ 80 | setMaxContextLines(state.getAttributeValue(MAX_CONTEXT_LINES_TAG)); 81 | } 82 | if(state.getAttributeValue(FIM_MODEL_TAG)!=null){ 83 | setFimTokenModel(PromptModel.fromId(state.getAttributeValue(FIM_MODEL_TAG))); 84 | } 85 | } 86 | 87 | public static StarCoderSettings getInstance() { 88 | if (ApplicationManager.getApplication() == null) { 89 | return starCoderSettingsInstance; 90 | } 91 | 92 | StarCoderSettings service = ApplicationManager.getApplication().getService(StarCoderSettings.class); 93 | if(service == null) { 94 | return starCoderSettingsInstance; 95 | } 96 | return service; 97 | } 98 | 99 | public boolean isSaytEnabled() { 100 | return saytEnabled; 101 | } 102 | 103 | public void setSaytEnabled(boolean saytEnabled) { 104 | this.saytEnabled = saytEnabled; 105 | } 106 | 107 | public void toggleSaytEnabled() { 108 | this.saytEnabled = !this.saytEnabled; 109 | } 110 | 111 | public String getApiURL() { 112 | return apiURL; 113 | } 114 | 115 | public void setApiURL(String apiURL) { 116 | this.apiURL = apiURL; 117 | } 118 | 119 | public String getApiToken() { 120 | Credentials credentials = PasswordSafe.getInstance().get(CREDENTIAL_ATTRIBUTES); 121 | return credentials != null ? credentials.getPasswordAsString() : ""; 122 | } 123 | 124 | public void setApiToken(String apiToken) { 125 | PasswordSafe.getInstance().set(CREDENTIAL_ATTRIBUTES, new Credentials(null, apiToken)); 126 | } 127 | 128 | public float getTemperature() { 129 | return temperature; 130 | } 131 | 132 | public void setTemperature(String temperature) { 133 | this.temperature = Float.parseFloat(temperature); 134 | } 135 | 136 | public int getMaxNewTokens() { 137 | return maxNewTokens; 138 | } 139 | 140 | public void setMaxNewTokens(String maxNewTokens) { 141 | this.maxNewTokens = Integer.parseInt(maxNewTokens); 142 | } 143 | 144 | public float getTopP() { 145 | return topP; 146 | } 147 | 148 | public void setTopP(String topP) { 149 | this.topP = Float.parseFloat(topP); 150 | } 151 | 152 | public float getRepetitionPenalty() { 153 | return repetitionPenalty; 154 | } 155 | 156 | public void setRepetitionPenalty(String repetitionPenalty) { 157 | this.repetitionPenalty = Float.parseFloat(repetitionPenalty); 158 | } 159 | 160 | public int getMaxContextLines() { 161 | return maxContextLines; 162 | } 163 | 164 | public void setMaxContextLines(String maxContextLines) { 165 | this.maxContextLines = Integer.parseInt(maxContextLines); 166 | } 167 | 168 | public TabActionOption getTabActionOption() { 169 | return tabActionOption; 170 | } 171 | 172 | public void setTabActionOption(TabActionOption tabActionOption) { 173 | this.tabActionOption = tabActionOption; 174 | } 175 | 176 | public PromptModel getFimTokenModel(){ 177 | return fimTokenModel; 178 | } 179 | 180 | public void setFimTokenModel(PromptModel fimTokenModel){ 181 | this.fimTokenModel=fimTokenModel; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/CodeGenHintRenderer.kt: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder 2 | 3 | import com.intellij.ide.ui.AntialiasingType 4 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors 5 | import com.intellij.openapi.editor.Editor 6 | import com.intellij.openapi.editor.EditorCustomElementRenderer 7 | import com.intellij.openapi.editor.Inlay 8 | import com.intellij.openapi.editor.colors.EditorFontType 9 | import com.intellij.openapi.editor.impl.EditorImpl 10 | import com.intellij.openapi.editor.impl.FocusModeModel 11 | import com.intellij.openapi.editor.impl.FontInfo 12 | import com.intellij.openapi.editor.markup.EffectType 13 | import com.intellij.openapi.editor.markup.TextAttributes 14 | import com.intellij.ui.paint.EffectPainter 15 | import com.intellij.util.ui.GraphicsUtil 16 | import com.intellij.util.ui.UIUtil 17 | import java.awt.* 18 | import kotlin.math.abs 19 | import kotlin.math.roundToInt 20 | 21 | open class CodeGenHintRenderer(var text: String?) : EditorCustomElementRenderer { 22 | 23 | override fun calcWidthInPixels(inlay: Inlay<*>): Int { 24 | return calcWidthInPixels(inlay.editor, text) 25 | } 26 | 27 | protected open fun getTextAttributes(editor: Editor): TextAttributes? { 28 | return editor.colorsScheme.getAttributes(DefaultLanguageHighlighterColors.LINE_COMMENT) 29 | } 30 | 31 | override fun paint(inlay: Inlay<*>, g: Graphics, r: Rectangle, textAttributes: TextAttributes) { 32 | val editor = inlay.editor 33 | if (editor !is EditorImpl) return 34 | 35 | val focusModeRange = editor.focusModeRange 36 | val attributes = if (focusModeRange != null && (inlay.offset <= focusModeRange.startOffset || focusModeRange.endOffset <= inlay.offset)) { 37 | editor.getUserData(FocusModeModel.FOCUS_MODE_ATTRIBUTES) ?: getTextAttributes(editor) 38 | } 39 | else { 40 | getTextAttributes(editor) 41 | } 42 | 43 | paintHint(g, editor, r, text, attributes, textAttributes) 44 | } 45 | 46 | companion object { 47 | @JvmStatic 48 | fun calcWidthInPixels(editor: Editor, text: String?): Int { 49 | val fontMetrics = getFontMetrics(editor) 50 | return calcHintTextWidth(text, fontMetrics) 51 | } 52 | 53 | @JvmStatic 54 | fun paintHint(g: Graphics, 55 | editor: EditorImpl, 56 | r: Rectangle, 57 | text: String?, 58 | attributes: TextAttributes?, 59 | textAttributes: TextAttributes) { 60 | val ascent = editor.ascent 61 | val descent = editor.descent 62 | val g2d = g as Graphics2D 63 | 64 | if (text != null && attributes != null) { 65 | val gap = 0 66 | val backgroundColor = attributes.backgroundColor 67 | if (backgroundColor != null) { 68 | val alpha = if (isInsufficientContrast(attributes, textAttributes)) 1.0f else BACKGROUND_ALPHA 69 | val config = GraphicsUtil.setupAAPainting(g) 70 | GraphicsUtil.paintWithAlpha(g, alpha) 71 | g.setColor(backgroundColor) 72 | g.fillRoundRect(r.x + 2, r.y + gap, r.width - 4, r.height - gap * 2, 8, 8) 73 | config.restore() 74 | } 75 | val foregroundColor = attributes.foregroundColor 76 | if (foregroundColor != null) { 77 | val savedHint = g2d.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING) 78 | val savedClip = g.getClip() 79 | 80 | g.setColor(foregroundColor) 81 | g.setFont(getFont(editor)) 82 | g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AntialiasingType.getKeyForCurrentScope(false)) 83 | g.clipRect(r.x + 3, r.y + 2, r.width - 6, r.height - 4) 84 | 85 | val startX = r.x + 2 86 | val startY = r.y + ascent 87 | g.drawString(text, startX, startY) 88 | g.setClip(savedClip) 89 | g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, savedHint) 90 | } 91 | } 92 | val effectColor = textAttributes.effectColor 93 | val effectType = textAttributes.effectType 94 | if (effectColor != null) { 95 | g.setColor(effectColor) 96 | val xStart = r.x 97 | val xEnd = r.x + r.width 98 | val y = r.y + ascent 99 | val font = editor.getColorsScheme().getFont(EditorFontType.PLAIN) 100 | when (effectType) { 101 | EffectType.LINE_UNDERSCORE -> EffectPainter.LINE_UNDERSCORE.paint(g2d, xStart, y, xEnd - xStart, descent, font) 102 | EffectType.BOLD_LINE_UNDERSCORE -> EffectPainter.BOLD_LINE_UNDERSCORE.paint(g2d, xStart, y, xEnd - xStart, descent, font) 103 | EffectType.STRIKEOUT -> EffectPainter.STRIKE_THROUGH.paint(g2d, xStart, y, xEnd - xStart, editor.charHeight, font) 104 | EffectType.WAVE_UNDERSCORE -> EffectPainter.WAVE_UNDERSCORE.paint(g2d, xStart, y, xEnd - xStart, descent, font) 105 | EffectType.BOLD_DOTTED_LINE -> EffectPainter.BOLD_DOTTED_UNDERSCORE.paint(g2d, xStart, y, xEnd - xStart, descent, font) 106 | else -> {} 107 | } 108 | } 109 | } 110 | 111 | private fun isInsufficientContrast( 112 | attributes: TextAttributes, 113 | surroundingAttributes: TextAttributes 114 | ): Boolean { 115 | val backgroundUnderHint = surroundingAttributes.backgroundColor 116 | if (backgroundUnderHint != null && attributes.foregroundColor != null) { 117 | val backgroundBlended = srcOverBlend(attributes.backgroundColor, backgroundUnderHint, BACKGROUND_ALPHA) 118 | 119 | val backgroundBlendedGrayed = backgroundBlended.toGray() 120 | val textGrayed = attributes.foregroundColor.toGray() 121 | val delta = abs(backgroundBlendedGrayed - textGrayed) 122 | return delta < 10 123 | } 124 | return false 125 | } 126 | 127 | private fun Color.toGray(): Double { 128 | return (0.30 * red) + (0.59 * green) + (0.11 * blue) 129 | } 130 | 131 | private fun srcOverBlend(foreground: Color, background: Color, foregroundAlpha: Float): Color { 132 | val r = foreground.red * foregroundAlpha + background.red * (1.0f - foregroundAlpha) 133 | val g = foreground.green * foregroundAlpha + background.green * (1.0f - foregroundAlpha) 134 | val b = foreground.blue * foregroundAlpha + background.blue * (1.0f - foregroundAlpha) 135 | return Color(r.roundToInt(), g.roundToInt(), b.roundToInt()) 136 | } 137 | 138 | @JvmStatic 139 | fun getFontMetrics(editor: Editor): FontMetrics { 140 | return FontInfo.getFontMetrics(getFont(editor), FontInfo.getFontRenderContext(editor.contentComponent)) 141 | } 142 | 143 | private fun getFont(editor: Editor): Font { 144 | val colorsScheme = editor.colorsScheme 145 | val attributes = editor.colorsScheme.getAttributes(DefaultLanguageHighlighterColors.LINE_COMMENT) 146 | 147 | // TODO override foreground/background color here? 148 | 149 | val fontStyle = attributes?.fontType ?: Font.PLAIN 150 | return UIUtil.getFontWithFallback(colorsScheme.getFont(EditorFontType.forJavaStyle(fontStyle))) 151 | } 152 | 153 | @JvmStatic 154 | protected fun calcHintTextWidth(text: String?, fontMetrics: FontMetrics): Int { 155 | return if (text == null) 0 else fontMetrics.stringWidth(text) + 14 156 | } 157 | 158 | private const val BACKGROUND_ALPHA = 0.55f 159 | } 160 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/main/resources/icons/widgetDisabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/widgetDisabled_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/actionIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/widgetEnabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/widgetError.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/actionIcon_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/widgetEnabled_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 John Phillips 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/settings/SettingsPanel.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 | 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 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 |
      229 | -------------------------------------------------------------------------------- /src/main/java/com/videogameaholic/intellij/starcoder/StarCoderWidget.java: -------------------------------------------------------------------------------- 1 | package com.videogameaholic.intellij.starcoder; 2 | 3 | import com.intellij.openapi.Disposable; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.command.WriteCommandAction; 6 | import com.intellij.openapi.editor.Document; 7 | import com.intellij.openapi.editor.Editor; 8 | import com.intellij.openapi.editor.EditorFactory; 9 | import com.intellij.openapi.editor.InlayModel; 10 | import com.intellij.openapi.editor.event.*; 11 | import com.intellij.openapi.editor.impl.EditorComponentImpl; 12 | import com.intellij.openapi.fileEditor.FileDocumentManager; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.openapi.util.Disposer; 15 | import com.intellij.openapi.util.Key; 16 | import com.intellij.openapi.util.NlsContexts; 17 | import com.intellij.openapi.util.TextRange; 18 | import com.intellij.openapi.vfs.VirtualFile; 19 | import com.intellij.openapi.wm.IdeFocusManager; 20 | import com.intellij.openapi.wm.StatusBar; 21 | import com.intellij.openapi.wm.StatusBarWidget; 22 | import com.intellij.openapi.wm.impl.status.EditorBasedWidget; 23 | import com.intellij.util.Consumer; 24 | import com.intellij.util.ui.update.MergingUpdateQueue; 25 | import com.intellij.util.ui.update.Update; 26 | import com.videogameaholic.intellij.starcoder.settings.StarCoderSettings; 27 | import org.jetbrains.annotations.NonNls; 28 | import org.jetbrains.annotations.NotNull; 29 | import org.jetbrains.annotations.Nullable; 30 | 31 | import javax.swing.*; 32 | import java.awt.*; 33 | import java.awt.event.MouseEvent; 34 | import java.beans.PropertyChangeEvent; 35 | import java.beans.PropertyChangeListener; 36 | import java.util.Arrays; 37 | 38 | public class StarCoderWidget extends EditorBasedWidget 39 | implements StatusBarWidget.Multiframe, StatusBarWidget.IconPresentation, 40 | CaretListener, SelectionListener, BulkAwareDocumentListener.Simple, PropertyChangeListener { 41 | public static final String ID = "StarCoderWidget"; 42 | 43 | public static final Key STAR_CODER_CODE_SUGGESTION = new Key<>("StarCoder Code Suggestion"); 44 | public static final Key STAR_CODER_POSITION = new Key<>("StarCoder Position"); 45 | 46 | private MergingUpdateQueue serviceQueue; 47 | 48 | protected StarCoderWidget(@NotNull Project project) { 49 | super(project); 50 | } 51 | 52 | @Override 53 | public @NonNls @NotNull String ID() { 54 | return ID; 55 | } 56 | 57 | @Override 58 | public StatusBarWidget copy() { 59 | return new StarCoderWidget(getProject()); 60 | } 61 | 62 | @Override 63 | public @Nullable Icon getIcon() { 64 | StarCoderService starCoder = ApplicationManager.getApplication().getService(StarCoderService.class); 65 | StarCoderStatus status = StarCoderStatus.getStatusByCode(starCoder.getStatus()); 66 | if(status == StarCoderStatus.OK) { 67 | return StarCoderSettings.getInstance().isSaytEnabled() ? StarCoderIcons.WidgetEnabled : StarCoderIcons.WidgetDisabled; 68 | } else { 69 | return StarCoderIcons.WidgetError; 70 | } 71 | } 72 | 73 | @Override 74 | public WidgetPresentation getPresentation() { 75 | return this; 76 | } 77 | 78 | @Override 79 | public @Nullable @NlsContexts.Tooltip String getTooltipText() { 80 | StringBuilder toolTipText = new StringBuilder("StarCoder"); 81 | if(StarCoderSettings.getInstance().isSaytEnabled()) { 82 | toolTipText.append(" enabled"); 83 | } else { 84 | toolTipText.append(" disabled"); 85 | } 86 | 87 | StarCoderService starCoder = ApplicationManager.getApplication().getService(StarCoderService.class); 88 | int statusCode = starCoder.getStatus(); 89 | StarCoderStatus status = StarCoderStatus.getStatusByCode(statusCode); 90 | switch (status) { 91 | case OK: 92 | if(StarCoderSettings.getInstance().isSaytEnabled()) { 93 | toolTipText.append(" (Click to disable)"); 94 | } else { 95 | toolTipText.append(" (Click to enable)"); 96 | } 97 | break; 98 | case UNKNOWN: 99 | toolTipText.append(" (http error "); 100 | toolTipText.append(statusCode); 101 | toolTipText.append(")"); 102 | break; 103 | default: 104 | toolTipText.append(" ("); 105 | toolTipText.append(status.getDisplayValue()); 106 | toolTipText.append(")"); 107 | } 108 | 109 | return toolTipText.toString(); 110 | } 111 | 112 | @Override 113 | public @Nullable Consumer getClickConsumer() { 114 | // Toggle if the plugin is enabled. 115 | return mouseEvent -> { 116 | StarCoderSettings.getInstance().toggleSaytEnabled(); 117 | if(myStatusBar != null) { 118 | myStatusBar.updateWidget(ID); 119 | } 120 | }; 121 | } 122 | 123 | @Override 124 | public void install(@NotNull StatusBar statusBar) { 125 | super.install(statusBar); 126 | serviceQueue = new MergingUpdateQueue("StarCoderServiceQueue",1000,true, 127 | null,this, null, false); 128 | serviceQueue.setRestartTimerOnAdd(true); 129 | EditorEventMulticaster multicaster = EditorFactory.getInstance().getEventMulticaster(); 130 | multicaster.addCaretListener(this, this); 131 | multicaster.addSelectionListener(this, this); 132 | multicaster.addDocumentListener(this,this); 133 | KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(SWING_FOCUS_OWNER_PROPERTY, this); 134 | Disposer.register(this, 135 | () -> KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(SWING_FOCUS_OWNER_PROPERTY, 136 | this) 137 | ); 138 | } 139 | 140 | private Editor getFocusOwnerEditor() { 141 | Component component = getFocusOwnerComponent(); 142 | Editor editor = component instanceof EditorComponentImpl ? ((EditorComponentImpl)component).getEditor() : getEditor(); 143 | return editor != null && !editor.isDisposed() ? editor : null; 144 | } 145 | 146 | private Component getFocusOwnerComponent() { 147 | Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); 148 | if (focusOwner == null) { 149 | IdeFocusManager focusManager = IdeFocusManager.getInstance(getProject()); 150 | Window frame = focusManager.getLastFocusedIdeWindow(); 151 | if (frame != null) { 152 | focusOwner = focusManager.getLastFocusedFor(frame); 153 | } 154 | } 155 | return focusOwner; 156 | } 157 | 158 | private boolean isFocusedEditor(Editor editor) { 159 | Component focusOwner = getFocusOwnerComponent(); 160 | return focusOwner == editor.getContentComponent(); 161 | } 162 | 163 | @Override 164 | public void propertyChange(PropertyChangeEvent evt) { 165 | updateInlayHints(getFocusOwnerEditor()); 166 | } 167 | 168 | @Override 169 | public void selectionChanged(SelectionEvent event) { 170 | updateInlayHints(event.getEditor()); 171 | } 172 | 173 | @Override 174 | public void caretPositionChanged(@NotNull CaretEvent event) { 175 | updateInlayHints(event.getEditor()); 176 | } 177 | 178 | @Override 179 | public void caretAdded(@NotNull CaretEvent event) { 180 | updateInlayHints(event.getEditor()); 181 | } 182 | 183 | @Override 184 | public void caretRemoved(@NotNull CaretEvent event) { 185 | updateInlayHints(event.getEditor()); 186 | } 187 | 188 | @Override 189 | public void afterDocumentChange (@NotNull Document document) { 190 | if(ApplicationManager.getApplication().isDispatchThread()) { 191 | EditorFactory.getInstance().editors(document) 192 | .filter(this::isFocusedEditor) 193 | .findFirst() 194 | .ifPresent(this::updateInlayHints); 195 | } 196 | } 197 | 198 | private void updateInlayHints(Editor focusedEditor) { 199 | if(focusedEditor == null) return; 200 | // TODO File extension exclusion settings? 201 | VirtualFile file = FileDocumentManager.getInstance().getFile(focusedEditor.getDocument()); 202 | if (file == null) return; 203 | 204 | // If a selection is highlighted, clear all hints. 205 | String selection = focusedEditor.getCaretModel().getCurrentCaret().getSelectedText(); 206 | if(selection != null && selection.length() > 0) { 207 | String[] existingHints = file.getUserData(STAR_CODER_CODE_SUGGESTION); 208 | if (existingHints != null && existingHints.length > 0) { 209 | file.putUserData(STAR_CODER_CODE_SUGGESTION, null); 210 | file.putUserData(STAR_CODER_POSITION, focusedEditor.getCaretModel().getOffset()); 211 | 212 | InlayModel inlayModel = focusedEditor.getInlayModel(); 213 | inlayModel.getInlineElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 214 | inlayModel.getBlockElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 215 | } 216 | return; 217 | } 218 | 219 | Integer starCoderPos = file.getUserData(STAR_CODER_POSITION); 220 | int lastPosition = (starCoderPos==null) ? 0 : starCoderPos; 221 | int currentPosition = focusedEditor.getCaretModel().getOffset(); 222 | 223 | // If cursor hasn't moved, don't do anything. 224 | if (lastPosition == currentPosition) return; 225 | 226 | // Check the existing inline hint (not blocks) if it exists. 227 | InlayModel inlayModel = focusedEditor.getInlayModel(); 228 | if (currentPosition > lastPosition) { 229 | String[] existingHints = file.getUserData(STAR_CODER_CODE_SUGGESTION); 230 | if (existingHints != null && existingHints.length > 0) { 231 | String inlineHint = existingHints[0]; 232 | String modifiedText = focusedEditor.getDocument().getCharsSequence().subSequence(lastPosition, currentPosition).toString(); 233 | if(modifiedText.startsWith("\n")) { 234 | // If the user typed Enter, the editor may have auto-spaced for alignment. 235 | modifiedText = modifiedText.replace(" ",""); 236 | // TODO Count the spaces and remove from the next block hint, or just remove 237 | // leading spaces from the block hint before moving up? 238 | // example: set a boolean here and do existingHints[1] = existingHints[1].stripLeading() 239 | // The problem is that the spaces are split in the update, some spaces are included after the carriage return, 240 | // (in the caret position update) but then after document change has more spaces in it. 241 | } 242 | // See if they typed the same thing that we suggested. 243 | if (inlineHint.startsWith(modifiedText)) { 244 | // Update the hint rather than calling the API to suggest a new one. 245 | inlineHint = inlineHint.substring(modifiedText.length()); 246 | if(inlineHint.length()>0) { 247 | // We only need to modify the inline hint and any block hints will remain unchanged. 248 | inlayModel.getInlineElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 249 | inlayModel.addInlineElement(currentPosition, true, new CodeGenHintRenderer(inlineHint)); 250 | existingHints[0] = inlineHint; 251 | 252 | // Update the UserData 253 | file.putUserData(STAR_CODER_CODE_SUGGESTION, existingHints); 254 | file.putUserData(STAR_CODER_POSITION, currentPosition); 255 | return; 256 | } else if (existingHints.length > 1) { 257 | // If the first line has been completely inserted, and there are more lines, move them up. 258 | existingHints = Arrays.copyOfRange(existingHints, 1, existingHints.length); 259 | addCodeSuggestion(focusedEditor, file, currentPosition, existingHints); 260 | return; 261 | } else { 262 | // We ran out of inline hint and there are no block hints, 263 | // So clear the hints now, and we'll call the API below. 264 | file.putUserData(STAR_CODER_CODE_SUGGESTION, null); 265 | } 266 | } 267 | } 268 | } 269 | 270 | // If we made it through all that, clear all hints and call the API. 271 | inlayModel.getInlineElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 272 | inlayModel.getBlockElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 273 | 274 | // Update position immediately to prevent repeated calls. 275 | file.putUserData(STAR_CODER_POSITION, currentPosition); 276 | 277 | StarCoderService starCoder = ApplicationManager.getApplication().getService(StarCoderService.class); 278 | 279 | Document currentDocument = focusedEditor.getDocument(); 280 | CharSequence currentContextWindow; 281 | int currentContextPosition; 282 | int maxLines = StarCoderSettings.getInstance().getMaxContextLines(); 283 | if(currentDocument.getLineCount()>maxLines && maxLines>0) { 284 | TextRange contextWindowRange = getContextWindowRange(currentDocument, currentPosition, maxLines); 285 | currentContextWindow = currentDocument.getCharsSequence().subSequence(contextWindowRange.getStartOffset(), contextWindowRange.getEndOffset()); 286 | // Update the current position based on current context 287 | currentContextPosition = currentPosition - contextWindowRange.getStartOffset(); 288 | } else { 289 | // Context is the entire document 290 | currentContextWindow = currentDocument.getCharsSequence(); 291 | currentContextPosition = currentPosition; 292 | } 293 | 294 | CharSequence contextWindow = currentContextWindow; 295 | int contextPosition = currentContextPosition; 296 | serviceQueue.queue(Update.create(focusedEditor,() -> { 297 | String[] hintList = starCoder.getCodeCompletionHints(contextWindow, contextPosition); 298 | this.addCodeSuggestion(focusedEditor, file, currentPosition, hintList); 299 | })); 300 | } 301 | 302 | private TextRange getContextWindowRange(Document editorDocument, int currentPosition, int maxLines) { 303 | int firstLine = 0; 304 | int lastLine = editorDocument.getLineCount() - 1; 305 | int currentLine = editorDocument.getLineNumber(currentPosition); 306 | 307 | if(currentLine>(maxLines/2)) { 308 | firstLine = currentLine-(maxLines/2); 309 | } 310 | if(lastLine>(currentLine+(maxLines/2))) { 311 | lastLine = (currentLine+(maxLines/2)); 312 | } 313 | if((lastLine-firstLine)0) { 317 | firstLine-=spareLines; 318 | } else { 319 | lastLine+=spareLines; 320 | } 321 | } 322 | 323 | return new TextRange(editorDocument.getLineStartOffset(firstLine), editorDocument.getLineEndOffset(lastLine)); 324 | } 325 | 326 | private void addCodeSuggestion(Editor focusedEditor, VirtualFile file, int suggestionPosition, String[] hintList) { 327 | WriteCommandAction.runWriteCommandAction(focusedEditor.getProject(), () -> { 328 | // Discard this update if the position has changed or text is now selected. 329 | if (suggestionPosition != focusedEditor.getCaretModel().getOffset()) return; 330 | if (focusedEditor.getSelectionModel().getSelectedText() != null) return; 331 | 332 | file.putUserData(STAR_CODER_CODE_SUGGESTION, hintList); 333 | file.putUserData(STAR_CODER_POSITION, suggestionPosition); 334 | 335 | InlayModel inlayModel = focusedEditor.getInlayModel(); 336 | inlayModel.getInlineElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 337 | inlayModel.getBlockElementsInRange(0, focusedEditor.getDocument().getTextLength(), CodeGenHintRenderer.class).forEach(Disposable::dispose); 338 | if (hintList != null && hintList.length > 0) { 339 | // The first line is an inline element 340 | if (hintList[0].trim().length() > 0) { 341 | inlayModel.addInlineElement(suggestionPosition, true, new CodeGenHintRenderer(hintList[0])); 342 | } 343 | // Each additional line is a block element 344 | for (int i = 1; i < hintList.length; i++) { 345 | inlayModel.addBlockElement(suggestionPosition, false, false, 0, new CodeGenHintRenderer(hintList[i])); 346 | } 347 | } 348 | }); 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------