Skip to content

Commit

Permalink
better aborts, more sophisticated rules for multiline
Browse files Browse the repository at this point in the history
  • Loading branch information
rjmacarthy committed Mar 19, 2024
1 parent 8af38ca commit 1f7485b
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 26 deletions.
11 changes: 7 additions & 4 deletions src/extension/parser-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Position, window } from 'vscode'
import path from 'path'
import { getIsOnlyBrackets } from './utils'
import { Logger } from '../common/logger'
import { getLineBreakCount } from '../webview/utils'
const logger = new Logger()

export const getParserForFile = async (
Expand Down Expand Up @@ -76,7 +77,8 @@ export function getNodeAtPosition(

const getIsErrorWithLexicalDeclaration = (node: SyntaxNode) => {
if (!node.hasError && node.text === '') return false
return node.type === 'lexical_declaration' && node.hasError
const lineCount = getLineBreakCount(node.text)
return node.type === 'lexical_declaration' && node.hasError && lineCount === 1
}

const getIsEmptyJsxNode = (node: SyntaxNode) => {
Expand Down Expand Up @@ -122,9 +124,10 @@ export const getIsEmptyMultiLineNode = (node: SyntaxNode) => {
}

export const getIsDeclarationType = (node: SyntaxNode) => {
return (
DECLARATION_TYPE.includes(node.text) || DECLARATION_TYPE.includes(node.type)
)
if (DECLARATION_TYPE.includes(node.text) || DECLARATION_TYPE.includes(node.type)) {
return true
}
return false
}

export const injectCompletionToNode = (
Expand Down
33 changes: 24 additions & 9 deletions src/extension/providers/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
getFimDataFromProvider,
getPrefixSuffix,
getShouldSkipCompletion,
isCursorInEmptyString
isCursorInEmptyString,
isMiddleWord as getIsMiddleOfWord
} from '../utils'
import { cache } from '../cache'
import { supportedLanguages } from '../../common/languages'
Expand Down Expand Up @@ -49,6 +50,7 @@ import {
export class CompletionProvider implements InlineCompletionItemProvider {
private _config = workspace.getConfiguration('twinny')
private _abortController: AbortController | null
private _acceptedLastCompletion = false
private _apiHostname = this._config.get('apiHostname') as string
private _apiPath = this._config.get('fimApiPath') as string
private _apiProvider = this._config.get('apiProvider') as string
Expand All @@ -67,6 +69,8 @@ export class CompletionProvider implements InlineCompletionItemProvider {
private _fimModel = this._config.get('fimModelName') as string
private _fimTemplateFormat = this._config.get('fimTemplateFormat') as string
private _keepAlive = this._config.get('keepAlive') as string | number
private _lastCompletionText = ''
private _lastCompletionIsMultiLine = false
private _lock: AsyncLock
private _logger: Logger
private _nonce = 0
Expand Down Expand Up @@ -133,7 +137,10 @@ export class CompletionProvider implements InlineCompletionItemProvider {
this._isMultiLineCompletion =
isMultiLineCompletionNode && !isInMiddleOfString

if (this.isMiddleWord(prefixSuffix)) {
const isMultiLineAndAcceptedLast =
this._lastCompletionIsMultiLine && this._acceptedLastCompletion

if (getIsMiddleOfWord() || isMultiLineAndAcceptedLast) {
return []
}

Expand Down Expand Up @@ -176,11 +183,16 @@ export class CompletionProvider implements InlineCompletionItemProvider {
})
}

private isMiddleWord(prefixSuffix: PrefixSuffix) {
const { prefix, suffix } = prefixSuffix
return (
/\w/.test(prefix.at(-1) as string) && /\w/.test(suffix.at(0) as string)
)
public getLastCompletion = () => this._lastCompletionText

public getLastCompletionWasMultiLine = () => this._lastCompletionIsMultiLine

public setAcceptedLastCompletion(value: boolean) {
this._acceptedLastCompletion = value
}

public abortCompletion() {
this._abortController?.abort()
}

private buildStreamRequest(prompt: string) {
Expand Down Expand Up @@ -250,8 +262,8 @@ export class CompletionProvider implements InlineCompletionItemProvider {
this._chunkCount = this._chunkCount + 1

if (
!this._useMultiLineCompletions &&
this._chunkCount >= 1 &&
(!this._useMultiLineCompletions || !this._isMultiLineCompletion) &&
this._chunkCount >= 2 &&
LINE_BREAK_REGEX.test(this._completion)
) {
this._logger.log(
Expand All @@ -274,6 +286,7 @@ export class CompletionProvider implements InlineCompletionItemProvider {
) {
this._completion = this._validCompletion
this.removeStopWords()
this._abortController?.abort()
return done(this.triggerInlineCompletion(prefixSuffix))
}
} catch (e) {
Expand Down Expand Up @@ -412,6 +425,8 @@ export class CompletionProvider implements InlineCompletionItemProvider {
)

this._statusBar.text = '🤖'
this._lastCompletionText = insertText
this._lastCompletionIsMultiLine = this._isMultiLineCompletion

return [
new InlineCompletionItem(
Expand Down
37 changes: 28 additions & 9 deletions src/extension/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,13 @@ export const getPrefixSuffix = (
}
}

export const isCursorInEmptyString = () => {
export const getBeforeAndAfter = (matcher: (char: string) => boolean) => {
const editor = window.activeTextEditor
if (!editor) return false
if (!editor)
return {
charBefore: '',
charAfter: ''
}

const position = editor.selection.active
const lineText = editor.document.lineAt(position.line).text
Expand All @@ -201,18 +205,33 @@ export const isCursorInEmptyString = () => {
.substring(0, position.character)
.split('')
.reverse()
.find((char) => QUOTES.includes(char))
.find(matcher)

const charAfter = lineText
.substring(position.character)
.split('')
.find((char) => QUOTES.includes(char))
.find(matcher)

return (
charBefore &&
charAfter &&
charBefore === charAfter &&
QUOTES.includes(charBefore)
return {
charBefore,
charAfter
}
}

export const isMiddleWord = () => {
const { charBefore, charAfter } = getBeforeAndAfter((char: string) => {
return /\w/.test(char)
})

return charBefore && charAfter && QUOTES?.includes(charAfter)
}

export const isCursorInEmptyString = () => {
const { charBefore, charAfter } = getBeforeAndAfter((char) =>
QUOTES.includes(char)
)

return charBefore && charAfter
}

export const getTheme = () => {
Expand Down
26 changes: 22 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ export async function activate(context: ExtensionContext) {
const templateDir = path.join(os.homedir(), '.twinny/templates') as string
const templateProvider = new TemplateProvider(templateDir)
const fileInteractionCache = new FileInteractionCache()
const completionProvider = new CompletionProvider(statusBar, fileInteractionCache)
const sidebarProvider = new SidebarProvider(
const completionProvider = new CompletionProvider(
statusBar,
context,
templateDir,
fileInteractionCache
)
const sidebarProvider = new SidebarProvider(statusBar, context, templateDir)

templateProvider.init()

Expand Down Expand Up @@ -161,12 +160,31 @@ export async function activate(context: ExtensionContext) {
workspace.onDidChangeTextDocument((e) => {
const changes = e.contentChanges[0]
if (!changes) return
const lastCompletion = completionProvider.getLastCompletion()
const lastCompletionWasMultiLine =
completionProvider.getLastCompletionWasMultiLine()

if (
changes.text &&
lastCompletion &&
changes.text === lastCompletion &&
lastCompletionWasMultiLine
) {
completionProvider.setAcceptedLastCompletion(true)
}
const currentLine = changes.range.start.line
const currentCharacter = changes.range.start.character
fileInteractionCache.incrementStrokes(currentLine, currentCharacter)
})
)

window.onDidChangeTextEditorSelection(() => {
completionProvider.abortCompletion()
delayExecution(() => {
completionProvider.setAcceptedLastCompletion(false)
}, 200)
})

context.subscriptions.push(
workspace.onDidChangeConfiguration((event) => {
if (!event.affectsConfiguration('twinny')) return
Expand Down

0 comments on commit 1f7485b

Please sign in to comment.