Skip to content

Nate/dev #1739

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/config/ConfigHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ export class ConfigHandler {
this.selectedProfileId = profileId;
const newConfig = await this.loadConfig();
this.notifyConfigListerners(newConfig);
const selectedProfiles =
this.globalContext.get("lastSelectedProfileForWorkspace") ?? {};
selectedProfiles[await this.getWorkspaceId()] = profileId;
this.globalContext.update(
"lastSelectedProfileForWorkspace",
selectedProfiles,
);
}

// A unique ID for the current workspace, built from folder names
Expand Down
5 changes: 0 additions & 5 deletions core/config/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ export function setupApiKeysMode(
return {
...config,
models: config.models.filter((model) => model.provider !== "free-trial"),
tabAutocompleteModel: {
title: "Tab Autocomplete",
provider: "free-trial",
model: TRIAL_FIM_MODEL,
},
embeddingsProvider: {
provider: "free-trial",
},
Expand Down
8 changes: 6 additions & 2 deletions core/llm/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { findLlmInfo } from "@continuedev/llm-info";
import Handlebars from "handlebars";
import {
ChatMessage,
Expand Down Expand Up @@ -114,14 +115,17 @@ export abstract class BaseLLM implements ILLM {
..._options,
};

this.model = options.model;
const llmInfo = findLlmInfo(this.model);

const templateType =
options.template ?? autodetectTemplateType(options.model);

this.title = options.title;
this.uniqueId = options.uniqueId ?? "None";
this.model = options.model;
this.systemMessage = options.systemMessage;
this.contextLength = options.contextLength ?? DEFAULT_CONTEXT_LENGTH;
this.contextLength =
options.contextLength ?? llmInfo?.contextLength ?? DEFAULT_CONTEXT_LENGTH;
this.completionOptions = {
...options.completionOptions,
model: options.model || "gpt-4",
Expand Down
4 changes: 2 additions & 2 deletions core/llm/templates/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ ${otherData.codeToEdit}
${suffixTag}
\`\`\`

Please rewrite the entire code block above in order to satisfy the following request: "${otherData.userInput}".${suffixExplanation}`,
Please rewrite the entire code block above in order to satisfy the following request: "${otherData.userInput}". You should rewrite the entire code block without leaving placeholders, even if the code is the same as before.${suffixExplanation}`,
},
{
role: "assistant",
Expand All @@ -115,7 +115,7 @@ ${otherData.codeToEdit}
${suffixTag}
\`\`\`

Please rewrite the entire code block above, editing the portion below "${START_TAG}" in order to satisfy the following request: "${otherData.userInput}".${suffixExplanation}
Please rewrite the entire code block above, editing the portion below "${START_TAG}" in order to satisfy the following request: "${otherData.userInput}". You should rewrite the entire code block without leaving placeholders, even if the code is the same as before.${suffixExplanation}
`,
},
{
Expand Down
6 changes: 6 additions & 0 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@aws-sdk/client-bedrock-runtime": "^3.574.0",
"@aws-sdk/credential-providers": "^3.596.0",
"@continuedev/config-types": "^1.0.6",
"@continuedev/llm-info": "^1.0.1",
"@mozilla/readability": "^0.5.0",
"@octokit/rest": "^20.0.2",
"@types/jsdom": "^21.1.6",
Expand Down
7 changes: 4 additions & 3 deletions core/util/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,17 @@ function getMigrationsFolderPath(): string {
return migrationsPath;
}

export function migrate(
export async function migrate(
id: string,
callback: () => void,
callback: () => void | Promise<void>,
onAlreadyComplete?: () => void,
) {
const migrationsPath = getMigrationsFolderPath();
const migrationPath = path.join(migrationsPath, id);

if (!fs.existsSync(migrationPath)) {
try {
callback();
await Promise.resolve(callback());
fs.writeFileSync(migrationPath, "");
} catch (e) {
console.error(`Migration ${id} failed`, e);
Expand Down
20 changes: 12 additions & 8 deletions docs/docs/customization/context-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ As an example, say you are working on solving a new GitHub Issue. You type '@iss

To use any of the built-in context providers, open `~/.continue/config.json` and add it to the `contextProviders` list.

### Files

Type '@file' to reference any file in your current workspace.

```json
{ "name": "file" }
```

### Code

Type '@code' to reference specific functions or classes from throughout your project.
Expand Down Expand Up @@ -498,9 +506,7 @@ Continue exposes an API for registering context providers from a 3rd party VSCod

```json
{
"extensionDependencies": [
"continue.continue"
],
"extensionDependencies": ["continue.continue"]
}
```

Expand All @@ -513,7 +519,6 @@ Here is an example:
import * as vscode from "vscode";

class MyCustomProvider implements IContextProvider {

get description(): ContextProviderDescription {
return {
title: "custom",
Expand All @@ -525,7 +530,7 @@ class MyCustomProvider implements IContextProvider {

async getContextItems(
query: string,
extras: ContextProviderExtras
extras: ContextProviderExtras,
): Promise<ContextItem[]> {
return [
{
Expand All @@ -537,7 +542,7 @@ class MyCustomProvider implements IContextProvider {
}

async loadSubmenuItems(
args: LoadSubmenuItemsArgs
args: LoadSubmenuItemsArgs,
): Promise<ContextSubmenuItem[]> {
return [];
}
Expand All @@ -554,5 +559,4 @@ const continueApi = continueExt?.exports;

// register your custom provider
continueApi?.registerCustomContextProvider(customProvider);

```
```
2 changes: 1 addition & 1 deletion extensions/intellij/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.continuedev.continueintellijextension
pluginName = continue-intellij-extension
pluginRepositoryUrl = https://github.com/continuedev/continue
# SemVer format -> https://semver.org
pluginVersion = 0.0.53
pluginVersion = 0.0.54

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 223
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.continuedev.continueintellijextension.autocomplete

import com.github.continuedev.continueintellijextension.services.ContinueExtensionSettings
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.components.service

class AutocompleteActionGroup : DefaultActionGroup() {
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.EDT
}

override fun update(e: AnActionEvent) {
super.update(e)
removeAll()

val continueSettingsService = service<ContinueExtensionSettings>()
if (continueSettingsService.continueState.enableTabAutocomplete) {
addAll(
DisableTabAutocompleteAction(),
)
} else {
addAll(
EnableTabAutocompleteAction(),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import com.intellij.openapi.util.TextRange
class AutocompleteCaretListener: CaretListener {
override fun caretPositionChanged(event: CaretEvent) {
val caret = event.caret ?: return
val oldPosition = event.oldPosition
val offset = caret.offset
val editor = caret.editor
val autocompleteService = editor.project?.service<AutocompleteService>() ?: return

if (autocompleteService.lastChangeWasPartialAccept) {
autocompleteService.lastChangeWasPartialAccept = false
return
}

val pending = autocompleteService.pendingCompletion;
if (pending != null && pending.editor == editor && pending.offset == offset) {
return
Expand All @@ -29,10 +34,16 @@ class AutocompleteDocumentListener(private val editorManager: FileEditorManager,
if (editor != editorManager.selectedTextEditor) {
return
}

val service = editor.project?.service<AutocompleteService>() ?: return
if (service.lastChangeWasPartialAccept) {
return
}

// Invoke later is important, otherwise the completion will be triggered before the document is updated
// causing the old caret offset to be used
invokeLater {
editor.project?.service<AutocompleteService>()?.triggerCompletion(editor)
service.triggerCompletion(editor)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,29 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.InlayProperties
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.WindowManager

data class PendingCompletion (
val editor: Editor,
val offset: Int,
val completionId: String,
val text: String?
val editor: Editor,
var offset: Int,
val completionId: String,
var text: String?
)

@Service(Service.Level.PROJECT)
class AutocompleteService(private val project: Project) {
var pendingCompletion: PendingCompletion? = null;
private val autocompleteLookupListener = project.service<AutocompleteLookupListener>()
private var widget: AutocompleteSpinnerWidget? = null

// To avoid triggering another completion on partial acceptance,
// we need to keep track of whether the last change was a partial accept
var lastChangeWasPartialAccept = false

init {
val statusBar = WindowManager.getInstance().getStatusBar(project)
widget = statusBar.getWidget("AutocompleteSpinnerWidget") as? AutocompleteSpinnerWidget
}

fun triggerCompletion(editor: Editor) {
val settings =
Expand All @@ -42,6 +53,7 @@ class AutocompleteService(private val project: Project) {
val completionId = uuid()
val offset = editor.caretModel.primaryCaret.offset
pendingCompletion = PendingCompletion(editor, offset, completionId, null)
widget?.setLoading(true)

// Request a completion from the core
val virtualFile = FileDocumentManager.getInstance().getFile(editor.document)
Expand All @@ -63,11 +75,13 @@ class AutocompleteService(private val project: Project) {
val lineLength = lineEnd - lineStart

project.service<ContinuePluginService>().coreMessenger?.request("autocomplete/complete", input, null, ({ response ->
widget?.setLoading(false)

val completions = response as List<*>
if (completions.isNotEmpty()) {
val completion = completions[0].toString()

if (completion.lines().size === 1 || column >= lineLength) {
if (completion.isNotEmpty() && (completion.lines().size === 1 || column >= lineLength)) {
// Do not render if completion is multi-line and caret is in middle of line
renderCompletion(editor, offset, completion)
pendingCompletion = pendingCompletion?.copy(text = completion)
Expand All @@ -80,12 +94,18 @@ class AutocompleteService(private val project: Project) {
}

private fun renderCompletion(editor: Editor, offset: Int, text: String) {
if (text.isEmpty()) {
return
}
// Don't render completions when code completion dropdown is visible
if (!autocompleteLookupListener.isLookupEmpty()) {
return
}
ApplicationManager.getApplication().invokeLater {
WriteAction.run<Throwable> {
// Clear existing completions
hideCompletions(editor)

val properties = InlayProperties()
properties.relatesToPrecedingText(true)
properties.disableSoftWrapping(true)
Expand Down Expand Up @@ -120,8 +140,57 @@ class AutocompleteService(private val project: Project) {
}
}

private fun splitKeepingDelimiters(input: String, delimiterPattern: String = "\\s+"): List<String> {
val initialSplit = input.split("(?<=$delimiterPattern)|(?=$delimiterPattern)".toRegex())
.filter { it.isNotEmpty() }

val result = mutableListOf<String>()
var currentDelimiter = ""

for (part in initialSplit) {
if (part.matches(delimiterPattern.toRegex())) {
currentDelimiter += part
} else {
if (currentDelimiter.isNotEmpty()) {
result.add(currentDelimiter)
currentDelimiter = ""
}
result.add(part)
}
}

if (currentDelimiter.isNotEmpty()) {
result.add(currentDelimiter)
}

return result
}

fun partialAccept() {
val completion = pendingCompletion ?: return
val text = completion.text ?: return
val editor = completion.editor
val offset = completion.offset

lastChangeWasPartialAccept = true

// Split the text into words, keeping delimiters
val words = splitKeepingDelimiters(text)
println(words)
val word = words[0]
editor.document.insertString(offset, word)
editor.caretModel.moveToOffset(offset + word.length)

// Remove the completion and re-display it
hideCompletions(editor)
completion.text = text.substring(word.length)
completion.offset += word.length
renderCompletion(editor, completion.offset, completion.text!!)
}

private fun cancelCompletion(completion: PendingCompletion) {
// Send cancellation message to core
widget?.setLoading(false)
project.service<ContinuePluginService>().coreMessenger?.request("autocomplete/cancel", null,null, ({}))
}

Expand Down
Loading
Loading