Skip to content

Commit

Permalink
Merge pull request #188 from rjmacarthy/development
Browse files Browse the repository at this point in the history
Inline completion fixes
  • Loading branch information
rjmacarthy committed Mar 25, 2024
2 parents 42884f8 + 6345d8a commit ec71881
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 76 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ For FIM completions, you need to use LLM models called "base models". Unlike ins
If using Llama the model must support the Llama special tokens.

- For computers with a good GPU, use: `deepseek-coder:base` or `codellama-code` (or any other good model that is optimised for code completions).
- For slower computers or computers using only CPU, use `stable-code:3b-code-q4_0` (or any other small base model).
- For slower computers or computers using only CPU, use `deepseek-coder:1.3b-base-q4_1` (or any other small base model).


## Keyboard shortcuts
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "twinny",
"displayName": "twinny - AI Code Completion and Chat",
"description": "Locally hosted AI code completion plugin for vscode",
"version": "3.8.18",
"version": "3.8.19",
"icon": "assets/icon.png",
"keywords": [
"code-inference",
Expand Down
3 changes: 2 additions & 1 deletion src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const STOP_DEEPSEEK = [
'<|end▁of▁sentence|>'
]

export const STOP_STABLECODE = ['<|endoftext|>']
export const STOP_STARCODER = ['<|endoftext|>']

export const API_PROVIDER: ApiProviders = {
ollama: {
Expand Down Expand Up @@ -211,6 +211,7 @@ export const MULTI_LINE_NODE_TYPE = [
'object_type',
'interface_body',
'class_body',
'export_statement',
// react
'jsx_opening_element',
'jsx_self_closing_element',
Expand Down
3 changes: 2 additions & 1 deletion src/extension/completion-formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Position, Range, TextEditor } from 'vscode'

import { CLOSING_BRACKETS, OPENING_BRACKETS, QUOTES } from '../common/constants'
import { Bracket } from '../common/types'
import { getNoTextBeforeOrAfter } from './utils'
import { getIsOnlyBrackets, getNoTextBeforeOrAfter } from './utils'

export class CompletionFormatter {
private _characterAfterCursor: string
Expand Down Expand Up @@ -170,6 +170,7 @@ export class CompletionFormatter {
private preventDuplicateLines = (): CompletionFormatter => {
const lineCount = this._editor.document.lineCount
let nextLineIndex = this._cursorPosition.line + 1
if (getIsOnlyBrackets(this._normalisedCompletion)) return this
while (
nextLineIndex < this._cursorPosition.line + 3 &&
nextLineIndex < lineCount
Expand Down
13 changes: 10 additions & 3 deletions src/extension/fim-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
FIM_TEMPLATE_FORMAT,
STOP_DEEPSEEK,
STOP_LLAMA,
STOP_STABLECODE
STOP_STARCODER
} from '../common/constants'
import { supportedLanguages } from '../common/languages'
import { FimPromptTemplate } from '../common/types'
Expand Down Expand Up @@ -104,7 +104,10 @@ function getFimTemplateChosen(format: string, args: FimPromptTemplate) {
return getFimPromptTemplateDeepseek(args)
}

if (format === FIM_TEMPLATE_FORMAT.stableCode || format === FIM_TEMPLATE_FORMAT.starcoder) {
if (
format === FIM_TEMPLATE_FORMAT.stableCode ||
format === FIM_TEMPLATE_FORMAT.starcoder
) {
return getFimPromptTemplateStableCode(args)
}

Expand Down Expand Up @@ -147,7 +150,11 @@ export const getStopWordsAuto = (fimModel: string) => {
export const getStopWordsChosen = (format: string) => {
if (format === FIM_TEMPLATE_FORMAT.codellama) return STOP_LLAMA
if (format === FIM_TEMPLATE_FORMAT.deepseek) return STOP_DEEPSEEK
if (format === FIM_TEMPLATE_FORMAT.stableCode || format === FIM_TEMPLATE_FORMAT.starcoder) return STOP_STABLECODE
if (
format === FIM_TEMPLATE_FORMAT.stableCode ||
format === FIM_TEMPLATE_FORMAT.starcoder
)
return STOP_STARCODER
return STOP_LLAMA
}

Expand Down
5 changes: 4 additions & 1 deletion src/extension/parser-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ export const getIsEmptyMultiLineBlock = (node: SyntaxNode | null): boolean => {
const isOnlyBrackets = getIsOnlyBrackets(
node.children.map((n) => n.text).join('')
)
return isMultiLineType && (isOnlyBrackets || !node.hasError)
return (
(isMultiLineType && (isOnlyBrackets || !node.hasError)) ||
node.type === 'export_statement'
)
}

export const getOpenAndCloseBracketMatchJsx = (node: SyntaxNode | null) => {
Expand Down
17 changes: 9 additions & 8 deletions src/extension/providers/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
getFimDataFromProvider,
getPrefixSuffix,
getShouldSkipCompletion,
isCursorInEmptyString,
isMiddleWord as getIsMiddleOfWord
getIsMultiLineCompletion,
getIsMiddleWord
} from '../utils'
import { cache } from '../cache'
import { supportedLanguages } from '../../common/languages'
Expand All @@ -43,7 +43,7 @@ import Parser, { SyntaxNode } from 'web-tree-sitter'
import {
getIsMultiLineCompletionNode,
getNodeAtPosition,
getParserForFile,
getParserForFile
} from '../parser-utils'

export class CompletionProvider implements InlineCompletionItemProvider {
Expand Down Expand Up @@ -110,12 +110,12 @@ export class CompletionProvider implements InlineCompletionItemProvider {
this._parser = await getParserForFile(this._document.uri.fsPath)
const tree = this._parser?.parse(this._document.getText())
this._currentNode = getNodeAtPosition(tree, position)
const isInMiddleOfString = isCursorInEmptyString()
const isMultilineCompletion = getIsMultiLineCompletion()
const isMultiLineCompletionNode = getIsMultiLineCompletionNode(
this._currentNode
)
this._isMultiLineCompletion =
isMultiLineCompletionNode && !isInMiddleOfString
isMultiLineCompletionNode && isMultilineCompletion
}

public async provideInlineCompletionItems(
Expand All @@ -134,9 +134,8 @@ export class CompletionProvider implements InlineCompletionItemProvider {
position
)


if (
getIsMiddleOfWord() ||
getIsMiddleWord() ||
isLastCompletionAccepted ||
this._lastCompletionMultiline
) {
Expand Down Expand Up @@ -439,7 +438,9 @@ export class CompletionProvider implements InlineCompletionItemProvider {

if (!editor || !this._position) return []

const completionText = new CompletionFormatter(editor).format(this._completion)
const completionText = new CompletionFormatter(editor).format(
this._completion
)

if (this._cacheEnabled) cache.setCache(prefixSuffix, completionText)

Expand Down
90 changes: 32 additions & 58 deletions src/extension/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ import {
ALL_BRACKETS,
API_PROVIDER,
EXTENSION_NAME,
IMPORT_SEPARATOR,
PROVIDER_NAMES,
QUOTES,
SKIP_DECLARATION_SYMBOLS,
SKIP_IMPORT_KEYWORDS_AFTER
} from '../common/constants'
import { Logger } from '../common/logger'

Expand Down Expand Up @@ -85,9 +83,10 @@ export const getSkipVariableDeclataion = (
textAfter: string
) => {
if (
characterBefore &&
SKIP_DECLARATION_SYMBOLS.includes(characterBefore.trim()) &&
textAfter.length &&
!textAfter.at(0) as unknown as string === '?' &&
(!textAfter.at(0) as unknown as string) === '?' &&
!getIsOnlyBrackets(textAfter)
) {
return true
Expand All @@ -96,41 +95,6 @@ export const getSkipVariableDeclataion = (
return false
}

export const getSkipImportDeclaration = (
characterBefore: string,
textAfter: string
) => {
for (const skipWord of SKIP_IMPORT_KEYWORDS_AFTER) {
if (
textAfter.includes(skipWord) &&
!IMPORT_SEPARATOR.includes(characterBefore) &&
characterBefore !== ' '
) {
return true
}
}
return false
}

export const getCharacterBefore = (index = -1): string => {
const editor = window.activeTextEditor
if (!editor) return ''
const document = editor.document
const cursorPosition = editor.selection.active
const textBeforeRange = new Range(cursorPosition, new Position(0, 0))
const textBefore = document.getText(textBeforeRange)
const characterBefore = textBefore.at(index) as string

if (characterBefore === undefined) {
return SKIP_DECLARATION_SYMBOLS[0]
}

if (!characterBefore.trim()) {
return getCharacterBefore(index - 1)
}
return characterBefore
}

export const getShouldSkipCompletion = (
context: InlineCompletionContext,
disableAuto: boolean
Expand All @@ -142,9 +106,11 @@ export const getShouldSkipCompletion = (
const lineEndPosition = document.lineAt(cursorPosition.line).range.end
const textAfterRange = new Range(cursorPosition, lineEndPosition)
const textAfter = document.getText(textAfterRange)
const characterBefore = getCharacterBefore()
if (getSkipVariableDeclataion(characterBefore, textAfter)) return true
if (getSkipImportDeclaration(characterBefore, textAfter)) return true
const { charBefore } = getBeforeAndAfter()

if (getSkipVariableDeclataion(charBefore, textAfter)){
return true
}

return (
context.triggerKind === InlineCompletionTriggerKind.Automatic && disableAuto
Expand Down Expand Up @@ -191,7 +157,7 @@ export const getPrefixSuffix = (
}
}

export const getBeforeAndAfter = (matcher: (char: string) => boolean) => {
export const getBeforeAndAfter = () => {
const editor = window.activeTextEditor
if (!editor)
return {
Expand All @@ -204,37 +170,42 @@ export const getBeforeAndAfter = (matcher: (char: string) => boolean) => {

const charBefore = lineText
.substring(0, position.character)
.trim()
.split('')
.reverse()
.find(matcher)
.reverse()[0]

const charAfter = lineText
.substring(position.character)
.split('')
.find(matcher)
const charAfter = lineText.substring(position.character).trim().split('')[0]

return {
charBefore,
charAfter
}
}

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

return charBefore && charAfter && QUOTES?.includes(charAfter)
return (
charBefore && charAfter && /\w/.test(charBefore) && /\w/.test(charAfter)
)
}

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

return charBefore && charAfter
}

export const isCursorInEmptyString = () => {
const { charBefore, charAfter } = getBeforeAndAfter()

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

export const getIsMultiLineCompletion = () => {
return !getHasLineTextBeforeAndAfter() && !isCursorInEmptyString()
}

export const getTheme = () => {
const currentTheme = window.activeColorTheme
if (currentTheme.kind === ColorThemeKind.Light) {
Expand Down Expand Up @@ -309,7 +280,10 @@ export const getNoTextBeforeOrAfter = () => {
const editor = window.activeTextEditor
const cursorPosition = editor?.selection.active
if (!cursorPosition) return
const lastLinePosition = new Position(cursorPosition.line, editor.document.lineCount)
const lastLinePosition = new Position(
cursorPosition.line,
editor.document.lineCount
)
const textAfterRange = new Range(cursorPosition, lastLinePosition)
const textAfter = editor?.document.getText(textAfterRange)
const textBeforeRange = new Range(new Position(0, 0), cursorPosition)
Expand Down

0 comments on commit ec71881

Please sign in to comment.