language detection

This commit is contained in:
Andrew Pareles 2025-03-14 00:20:03 -07:00
parent c222a801a8
commit fa43a9f937
13 changed files with 348 additions and 248 deletions

View file

@ -204,7 +204,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ITextModelService private readonly _textModelService: ITextModelService,
@ITerminalToolService private readonly terminalToolService: ITerminalToolService,
@ITerminalToolService private readonly _terminalToolService: ITerminalToolService,
) {
super()
this.state = { allThreads: {}, currentThreadId: null as unknown as string } // default state
@ -226,7 +226,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
private _convertThreadDataFromStorage(threadsStr: string): ChatThreads {
return JSON.parse(threadsStr, (key, value) => {
if (value && typeof value === 'object' && value.$mid === 1) { // $mid is the MarshalledId. $mid === 1 means it is a URI
return URI.from(value);
return URI.from(value); // TODO URI.revive instead of this?
}
return value;
});
@ -400,7 +400,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// system message
const workspaceFolders = this._workspaceContextService.getWorkspace().folders.map(f => f.uri.fsPath)
const terminalIds = this.terminalToolService.listTerminalIds()
const terminalIds = this._terminalToolService.listTerminalIds()
const systemMessage = chat_systemMessage(workspaceFolders, terminalIds, chatMode)
// all messages so far in the chat history (including tools)

View file

@ -31,7 +31,6 @@ import { mountCtrlK } from './react/out/quick-edit-tsx/index.js'
import { QuickEditPropsType } from './quickEditActions.js';
import { IModelContentChangedEvent } from '../../../../editor/common/textModelEvents.js';
import { extractCodeFromFIM, extractCodeFromRegular, ExtractedSearchReplaceBlock, extractSearchReplaceBlocks } from '../common/helpers/extractCodeFromResult.js';
import { filenameToVscodeLanguage } from '../common/helpers/detectLanguage.js';
import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
import { isMacintosh } from '../../../../base/common/platform.js';
import { EditorOption } from '../../../../editor/common/config/editorOptions.js';
@ -46,6 +45,7 @@ import { IEditCodeService, URIStreamState, AddCtrlKOpts, StartApplyingOpts } fro
import { IVoidSettingsService } from '../common/voidSettingsService.js';
import { FeatureName } from '../common/voidSettingsTypes.js';
import { ILanguageService } from '../../../../editor/common/languages/language.js';
import { getFullLanguage, getLanguageFromModel } from '../common/helpers/getLanguage.js';
// import { IFileService } from '../../../../platform/files/common/files.js';
// import { VSBuffer } from '../../../../base/common/buffer.js';
@ -756,7 +756,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
const fileStr = await this._voidFileService.readFile(uri)
if (fileStr === null) return null
const lang = this._languageService.createByFilepathOrFirstLine(uri, fileStr?.split('\n')?.[0])
const lang = getFullLanguage(this._languageService, { uri, fileContents: fileStr })
const model = this._modelService.createModel(fileStr, lang, uri);
return model
}
@ -1279,7 +1279,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
const c_ = await this._voidFileService.readFile(uri)
if (c_ === null) return
currentFileStr = c_
console.log('got curent file', c_.length)
const numLines = numLinesOfStr(currentFileStr)
@ -1312,6 +1311,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
}
const originalCode = currentFileStr.split('\n').slice((startLine - 1), (endLine - 1) + 1).join('\n')
const language = getLanguageFromModel(uri, this._modelService)
let streamRequestIdRef: { current: string | null } = { current: null }
// promise that resolves when the apply is done
@ -1346,6 +1347,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
this._onDidChangeDiffZoneStreaming.fire({ uri, diffareaid: diffZone.diffareaid })
this._onDidAddOrDeleteDiffZones.fire({ uri })
if (from === 'QuickEdit') {
const { diffareaid } = opts
const ctrlKZone = this.diffAreaOfId[diffareaid]
@ -1359,7 +1362,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
let messages: LLMChatMessage[]
if (from === 'ClickApply') {
const userContent = rewriteCode_userMessage({ originalCode, applyStr: opts.applyStr, uri })
const userContent = rewriteCode_userMessage({ originalCode, applyStr: opts.applyStr, language })
messages = [
{ role: 'system', content: rewriteCode_systemMessage, },
{ role: 'user', content: userContent, }
@ -1373,7 +1376,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
const instructions = _mountInfo?.textAreaRef.current?.value ?? ''
const { prefix, suffix } = voidPrefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, isOllamaFIM: false, fimTags: quickEditFIMTags, language })
// type: 'messages',
messages = [

View file

@ -6,12 +6,13 @@
import React, { JSX, useState } from 'react'
import { marked, MarkedToken, Token } from 'marked'
import { BlockCode, BlockCodeWithApply } from './BlockCode.js'
import { nameToVscodeLanguage } from '../../../../common/helpers/detectLanguage.js'
import { convertToVscodeLang, getFirstLine, getLanguage } from '../../../../common/helpers/getLanguage.js'
import { useApplyButtonHTML } from './ApplyBlockHoverButtons.js'
import { useAccessor, useChatThreadsState } from '../util/services.js'
import { Range } from '../../../../../../services/search/common/searchExtTypes.js'
import { IRange } from '../../../../../../../base/common/range.js'
import { ScrollType } from '../../../../../../../editor/common/editorCommon.js'
import { URI } from '../../../../../../../base/common/uri.js'
export type ChatMessageLocation = {
@ -101,7 +102,9 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
export type RenderTokenOptions = { isApplyEnabled?: boolean, isLinkDetectionEnabled?: boolean }
const RenderToken = ({ token, nested, chatMessageLocation, tokenIdx, ...options }: { token: Token | string, nested?: boolean, chatMessageLocation?: ChatMessageLocation, tokenIdx: string, } & RenderTokenOptions): JSX.Element => {
const RenderToken = ({ token, inPTag, chatMessageLocation, tokenIdx, ...options }: { token: Token | string, inPTag?: boolean, chatMessageLocation?: ChatMessageLocation, tokenIdx: string, } & RenderTokenOptions): JSX.Element => {
const accessor = useAccessor()
const languageService = accessor.get('ILanguageService')
// deal with built-in tokens first (assume marked token)
const t = token as MarkedToken
@ -115,28 +118,41 @@ const RenderToken = ({ token, nested, chatMessageLocation, tokenIdx, ...options
}
if (t.type === "code") {
const [firstLine, remainingContents] = getFirstLine(t.text)
const firstLineIsURI = URI.isUri(firstLine)
const language = t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]
let language: string | undefined = undefined
if (t.lang !== undefined) {
// convert markdown language to language that vscode recognizes (eg markdown doesn't know bash but it does know shell)
language = convertToVscodeLang(languageService, t.lang)
}
// TODO user should only be able to apply this when the code has been closed (t.raw ends with "```")
else if (!language) { // if still no lang
if (firstLineIsURI) { // get lang from the uri
const uri = URI.file(firstLine)
language = getLanguage(languageService, { uri, fileContents: remainingContents ?? undefined })
}
else { // get lang from the contents
language = getLanguage(languageService, { uri: null, fileContents: remainingContents ?? undefined })
}
}
const contents = firstLineIsURI ? (remainingContents || '') : t.text // exclude first-line URI from contents
// TODO!!! user should only be able to apply this when the code has been closed (t.raw ends with "```")
if (options.isApplyEnabled && chatMessageLocation) {
const applyBoxId = getApplyBoxId({
threadId: chatMessageLocation.threadId,
messageIdx: chatMessageLocation.messageIdx,
tokenIdx: tokenIdx,
})
return <BlockCodeWithApply
initValue={t.text}
initValue={contents}
language={language}
applyBoxId={applyBoxId}
/>
}
return <BlockCode
initValue={t.text}
initValue={contents}
language={language}
/>
}
@ -223,7 +239,7 @@ const RenderToken = ({ token, nested, chatMessageLocation, tokenIdx, ...options
return <li>
<input type="checkbox" checked={t.checked} readOnly />
<span>
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={t.text} nested={true} {...options} />
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={t.text} inPTag={true} {...options} />
</span>
</li>
}
@ -239,7 +255,7 @@ const RenderToken = ({ token, nested, chatMessageLocation, tokenIdx, ...options
<input type="checkbox" checked={item.checked} readOnly />
)}
<span>
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={item.text} nested={true} {...options} />
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={item.text} inPTag={true} {...options} />
</span>
</li>
))}
@ -252,14 +268,15 @@ const RenderToken = ({ token, nested, chatMessageLocation, tokenIdx, ...options
{t.tokens.map((token, index) => (
<RenderToken key={index}
token={token}
tokenIdx={`${tokenIdx ? `${tokenIdx}-` : ''}${index}`} // assign a unique tokenId to nested components
tokenIdx={`${tokenIdx ? `${tokenIdx}-` : ''}${index}`} // assign a unique tokenId to inPTag components
chatMessageLocation={chatMessageLocation}
inPTag={true} // TODO!!! check this
{...options}
/>
))}
</>
if (nested) return contents
if (inPTag) return contents
return <p>
{contents}
@ -343,12 +360,13 @@ const RenderToken = ({ token, nested, chatMessageLocation, tokenIdx, ...options
)
}
export const ChatMarkdownRender = ({ string, nested = false, chatMessageLocation, ...options }: { string: string, nested?: boolean, chatMessageLocation: ChatMessageLocation | undefined } & RenderTokenOptions) => {
export const ChatMarkdownRender = ({ string, inPTag = false, chatMessageLocation, ...options }: { string: string, inPTag?: boolean, chatMessageLocation: ChatMessageLocation | undefined } & RenderTokenOptions) => {
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
return (
<>
{tokens.map((token, index) => (
<RenderToken key={index} token={token} nested={nested} chatMessageLocation={chatMessageLocation} tokenIdx={index + ''} {...options} />
<RenderToken key={index} token={token} inPTag={inPTag} chatMessageLocation={chatMessageLocation} tokenIdx={index + ''} {...options} />
))}
</>
)

View file

@ -25,11 +25,11 @@ import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js';
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
import { FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
import { filenameToVscodeLanguage } from '../../../../common/helpers/detectLanguage.js';
import { getModelSelectionState, getModelCapabilities } from '../../../../common/modelCapabilities.js';
import { AlertTriangle, ChevronRight, Dot, Pencil, X } from 'lucide-react';
import { ChatMessage, StagingSelectionItem, ToolMessage, ToolRequestApproval } from '../../../../common/chatThreadServiceTypes.js';
import { ToolCallParams, ToolName, ToolNameWithApproval } from '../../../../common/toolsServiceTypes.js';
import { getLanguageFromModel } from '../../../../common/helpers/getLanguage.js';
@ -505,6 +505,7 @@ export const SelectedFiles = (
const accessor = useAccessor()
const commandService = accessor.get('ICommandService')
const modelService = accessor.get('IModelService')
// state for tracking prospective files
const { currentUri } = useUriState()
@ -528,6 +529,7 @@ export const SelectedFiles = (
.map(uri => ({
type: 'File',
fileURI: uri,
language: getLanguageFromModel(uri, modelService),
selectionStr: null,
range: null,
state: { isOpened: false },
@ -638,7 +640,7 @@ export const SelectedFiles = (
>
<BlockCode
initValue={selection.selectionStr}
language={filenameToVscodeLanguage(selection.fileURI.path)}
language={selection.language}
maxHeight={200}
showScrollbars={true}
/>

View file

@ -882,9 +882,11 @@ export const VoidCodeEditor = ({ initValue, language, maxHeight, showScrollbars
}, [instantiationService])}
onCreateInstance={useCallback((editor: CodeEditorWidget) => {
const languageId = languageRef.current ? languageRef.current : 'plaintext'
const model = modelOfEditorId[id] ?? modelService.createModel(
initValueRef.current, {
languageId: languageRef.current ? languageRef.current : 'plaintext',
languageId: languageId,
onDidChange: (e) => { return { dispose: () => { } } } // no idea why they'd require this
})
modelRef.current = model

View file

@ -46,6 +46,7 @@ import { IMetricsService } from '../../../../../../../workbench/contrib/void/com
import { URI } from '../../../../../../../base/common/uri.js'
import { IChatThreadService, ThreadsState, ThreadStreamState } from '../../../chatThreadService.js'
import { ITerminalToolService } from '../../../terminalToolService.js'
import { ILanguageService } from '../../../../../../../editor/common/languages/language.js'
@ -225,7 +226,8 @@ const getReactAccessor = (accessor: ServicesAccessor) => {
IConfigurationService: accessor.get(IConfigurationService),
IPathService: accessor.get(IPathService),
IMetricsService: accessor.get(IMetricsService),
ITerminalToolService: accessor.get(ITerminalToolService)
ITerminalToolService: accessor.get(ITerminalToolService),
ILanguageService: accessor.get(ILanguageService),
} as const
return reactAccessor

View file

@ -123,12 +123,14 @@ registerAction2(class extends Action2 {
const selection: StagingSelectionItem = !selectionRange || !selectionStr || (selectionRange.startLineNumber > selectionRange.endLineNumber) ? {
type: 'File',
fileURI: model.uri,
language: model.getLanguageId(),
selectionStr: null,
range: null,
state: { isOpened: false, }
} : {
type: 'Selection',
fileURI: model.uri,
language: model.getLanguageId(),
selectionStr: selectionStr,
range: selectionRange,
state: { isOpened: true, }

View file

@ -50,6 +50,7 @@ export type ChatMessage =
export type CodeSelection = {
type: 'Selection';
fileURI: URI;
language: string;
selectionStr: string;
range: IRange;
state: {
@ -60,6 +61,7 @@ export type CodeSelection = {
export type FileSelection = {
type: 'File';
fileURI: URI;
language: string;
selectionStr: null;
range: null;
state: {

View file

@ -1,174 +0,0 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// eg "bash" -> "shell"
export const nameToVscodeLanguage: { [key: string]: string } = {
// Web Technologies
'html': 'html',
'css': 'css',
'scss': 'scss',
'sass': 'scss',
'less': 'less',
'javascript': 'typescript',
'js': 'typescript', // use more general renderer
'jsx': 'typescript',
'typescript': 'typescript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'python': 'python',
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'c++': 'cpp',
'c': 'c',
'csharp': 'csharp',
'cs': 'csharp',
'c#': 'csharp',
'go': 'go',
'golang': 'go',
'rust': 'rust',
'rs': 'rust',
'ruby': 'ruby',
'rb': 'ruby',
'php': 'php',
'shell': 'shell',
'bash': 'shell',
'sh': 'shell',
'zsh': 'shell',
// Markup and Config
'markdown': 'markdown',
'md': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Database and Query Languages
'sql': 'sql',
'mysql': 'sql',
'postgresql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
// Others
'dockerfile': 'dockerfile',
'docker': 'dockerfile',
'makefile': 'makefile',
'plaintext': 'plaintext',
'text': 'plaintext'
};
// eg ".ts" -> "typescript"
const fileExtensionToVscodeLanguage: { [key: string]: string } = {
// Web
'html': 'html',
'htm': 'html',
'css': 'css',
'scss': 'scss',
'less': 'less',
'js': 'javascript',
'jsx': 'javascript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'cc': 'cpp',
'c': 'c',
'h': 'cpp',
'hpp': 'cpp',
'cs': 'csharp',
'go': 'go',
'rs': 'rust',
'rb': 'ruby',
'php': 'php',
'sh': 'shell',
'bash': 'shell',
'zsh': 'shell',
// Markup/Config
'md': 'markdown',
'markdown': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Other
'sql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
'dockerfile': 'dockerfile',
'docker': 'dockerfile',
'mk': 'makefile',
// Config Files and Dot Files
'npmrc': 'ini',
'env': 'ini',
'gitignore': 'ignore',
'dockerignore': 'ignore',
'eslintrc': 'json',
'babelrc': 'json',
'prettierrc': 'json',
'stylelintrc': 'json',
'editorconfig': 'ini',
'htaccess': 'apacheconf',
'conf': 'ini',
'config': 'ini',
// Package Files
'package': 'json',
'package-lock': 'json',
'gemfile': 'ruby',
'podfile': 'ruby',
'rakefile': 'ruby',
// Build Systems
'cmake': 'cmake',
'makefile': 'makefile',
'gradle': 'groovy',
// Shell Scripts
'bashrc': 'shell',
'zshrc': 'shell',
'fish': 'shell',
// Version Control
'gitconfig': 'ini',
'hgrc': 'ini',
'svnconfig': 'ini',
// Web Server
'nginx': 'nginx',
// Misc Config
'properties': 'properties',
'cfg': 'ini',
'reg': 'ini'
};
export function filenameToVscodeLanguage(filename: string): string | undefined {
const ext = filename.toLowerCase().split('.').pop();
if (!ext) return undefined;
return fileExtensionToVscodeLanguage[ext];
}

View file

@ -0,0 +1,229 @@
// /*--------------------------------------------------------------------------------------
// * Copyright 2025 Glass Devtools, Inc. All rights reserved.
// * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
// *--------------------------------------------------------------------------------------*/
import { URI } from '../../../../../base/common/uri.js';
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
import { IModelService } from '../../../../../editor/common/services/model.js';
export const getFirstLine = (content: string): [string, string] | [string, undefined] => {
const newLineIdx = content.indexOf('\r\n')
if (newLineIdx !== -1) {
const A = content.substring(0, newLineIdx + 2)
const B = content.substring(newLineIdx + 2, Infinity);
return [A, B]
}
const newLineIdx2 = content.indexOf('\n')
if (newLineIdx2 !== -1) {
const A = content.substring(0, newLineIdx2 + 1)
const B = content.substring(newLineIdx2 + 1, Infinity);
return [A, B]
}
return [content, undefined]
}
export function getFullLanguage(languageService: ILanguageService, opts: { uri: URI | null, fileContents: string | undefined }) {
const firstLine = opts.fileContents ? getFirstLine(opts.fileContents)?.[0] : undefined
const fullLang = languageService.createByFilepathOrFirstLine(opts.uri, firstLine)
return fullLang
}
export function getLanguage(languageService: ILanguageService, opts: { uri: URI | null, fileContents: string | undefined }): string {
return getFullLanguage(languageService, opts).languageId
}
export function getLanguageFromModel(uri: URI, modelService: IModelService): string {
return modelService.getModel(uri)?.getLanguageId() || ''
}
// conversions
const convertToVoidLanguage = (languageService: ILanguageService, language: string) => {
const { languageId } = languageService.createById(language)
return languageId
}
export const convertToVscodeLang = (languageService: ILanguageService, markdownLang: string) => {
if (markdownLang in markdownLangToVscodeLang)
return markdownLangToVscodeLang[markdownLang]
return convertToVoidLanguage(languageService, markdownLang)
}
// // eg "bash" -> "shell"
const markdownLangToVscodeLang: { [key: string]: string } = {
// Web Technologies
'html': 'html',
'css': 'css',
'scss': 'scss',
'sass': 'scss',
'less': 'less',
'javascript': 'typescript',
'js': 'typescript', // use more general renderer
'jsx': 'typescript',
'typescript': 'typescript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'python': 'python',
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'c++': 'cpp',
'c': 'c',
'csharp': 'csharp',
'cs': 'csharp',
'c#': 'csharp',
'go': 'go',
'golang': 'go',
'rust': 'rust',
'rs': 'rust',
'ruby': 'ruby',
'rb': 'ruby',
'php': 'php',
'shell': 'shellscript', // this is important
'bash': 'shellscript',
'sh': 'shellscript',
'zsh': 'shellscript',
// Markup and Config
'markdown': 'markdown',
'md': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Database and Query Languages
'sql': 'sql',
'mysql': 'sql',
'postgresql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
// Others
'dockerfile': 'dockerfile',
'docker': 'dockerfile',
'makefile': 'makefile',
'plaintext': 'plaintext',
'text': 'plaintext'
};
// // eg ".ts" -> "typescript"
// const fileExtensionToVscodeLanguage: { [key: string]: string } = {
// // Web
// 'html': 'html',
// 'htm': 'html',
// 'css': 'css',
// 'scss': 'scss',
// 'less': 'less',
// 'js': 'javascript',
// 'jsx': 'javascript',
// 'ts': 'typescript',
// 'tsx': 'typescript',
// 'json': 'json',
// 'jsonc': 'json',
// // Programming Languages
// 'py': 'python',
// 'java': 'java',
// 'cpp': 'cpp',
// 'cc': 'cpp',
// 'c': 'c',
// 'h': 'cpp',
// 'hpp': 'cpp',
// 'cs': 'csharp',
// 'go': 'go',
// 'rs': 'rust',
// 'rb': 'ruby',
// 'php': 'php',
// 'sh': 'shell',
// 'bash': 'shell',
// 'zsh': 'shell',
// // Markup/Config
// 'md': 'markdown',
// 'markdown': 'markdown',
// 'xml': 'xml',
// 'svg': 'xml',
// 'yaml': 'yaml',
// 'yml': 'yaml',
// 'ini': 'ini',
// 'toml': 'ini',
// // Other
// 'sql': 'sql',
// 'graphql': 'graphql',
// 'gql': 'graphql',
// 'dockerfile': 'dockerfile',
// 'docker': 'dockerfile',
// 'mk': 'makefile',
// // Config Files and Dot Files
// 'npmrc': 'ini',
// 'env': 'ini',
// 'gitignore': 'ignore',
// 'dockerignore': 'ignore',
// 'eslintrc': 'json',
// 'babelrc': 'json',
// 'prettierrc': 'json',
// 'stylelintrc': 'json',
// 'editorconfig': 'ini',
// 'htaccess': 'apacheconf',
// 'conf': 'ini',
// 'config': 'ini',
// // Package Files
// 'package': 'json',
// 'package-lock': 'json',
// 'gemfile': 'ruby',
// 'podfile': 'ruby',
// 'rakefile': 'ruby',
// // Build Systems
// 'cmake': 'cmake',
// 'makefile': 'makefile',
// 'gradle': 'groovy',
// // Shell Scripts
// 'bashrc': 'shell',
// 'zshrc': 'shell',
// 'fish': 'shell',
// // Version Control
// 'gitconfig': 'ini',
// 'hgrc': 'ini',
// 'svnconfig': 'ini',
// // Web Server
// 'nginx': 'nginx',
// // Misc Config
// 'properties': 'properties',
// 'cfg': 'ini',
// 'reg': 'ini'
// };
// export function filenameToVscodeLanguage(filename: string): string | undefined {
// const ext = filename.toLowerCase().split('.').pop();
// if (!ext) return undefined;
// return fileExtensionToVscodeLanguage[ext];
// }

View file

@ -5,8 +5,6 @@
import { URI } from '../../../../../base/common/uri.js';
import { filenameToVscodeLanguage } from '../helpers/detectLanguage.js';
import { IModelService } from '../../../../../editor/common/services/model.js';
import { os } from '../helpers/systemInfo.js';
import { IVoidFileService } from '../voidFileService.js';
import { CodeSelection, FileSelection, StagingSelectionItem } from '../chatThreadServiceTypes.js';
@ -17,8 +15,8 @@ export const tripleTick = ['```', '```']
export const editToolDesc_toolDescription = `\
A high level description of the change you'd like to make in the file. This description will be handed to a dumber, faster model that will quickly apply the change. \
Typically the best description you can give here is a single code block of the form:\n${tripleTick[0]}\n// ... existing code ...\n{{change 1}}\n// ... existing code ...\n{{change2}}\n// ... existing code ...\n{{change 3}}\n...\n${tripleTick[1]}.\
Do NOT output the whole file here if possible, and try to write as LITTLE as needed to describe the change.`
Typically the best description you can give here is a single code block of the form:\n${tripleTick[0]}\n// ... existing code ...\n{{change 1}}\n// ... existing code ...\n{{change2}}\n// ... existing code ...\n{{change 3}}\n...\n${tripleTick[1]}. \
Do NOT output the whole file here if possible, and try to write as LITTLE code as needed to describe the change.`
@ -42,35 +40,37 @@ You will be given tools you can call.
- NEVER modify a file outside one of the the user's workspaces without confirmation from the user.`}
\
`: `\
You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.
\
You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.\
`}
${mode === 'agent' /* code blocks */ ? `\
If you have a change to make, you should almost always use a tool to edit the file. Even if you don't (e.g. if the user asks you not to), you should still NEVER re-write the entire file for the user. Instead, you should write comments like "// ... existing code" to indicate how to change the existing code.
If you have a change to make, you should almost always use a tool to edit the file. Even if you don't (e.g. if the user asks you not to), you should still NEVER re-write the entire file for the user. Instead, you should write comments like "// ... existing code" to indicate how to change the existing code. \
`: `\
If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S) (wrapped in triple backticks).
- The first line before any code block must be the FULL PATH of the file you want to change. If the path does not already exist, it will be created.
- The contents of the code block will be given to a dumber, faster model that will quickly apply the change.
- The first line of the code block must be the FULL PATH of the file you want to change. If the path does not already exist, it will be created.
- The remaining contents of the code block will be given to a dumber, faster model that will quickly apply the change.
- Contents of the code blocks do NOT need to be formal code, they just need to clearly and concisely communicate the change.
- Do NOT re-write the entire file in the code block(s). Instead, write comments like "// ... existing code" to indicate how to change the existing code.
- Do NOT re-write the entire file in the code block(s). Instead, write comments like "// ... existing code" to indicate how to change the existing code.`}
Misc:
- Always wrap any code you produce in triple backticks.
\
`}`
`
type FileSelnLocal = { fileURI: URI, content: string }
const stringifyFileSelection = ({ fileURI, content }: FileSelnLocal) => {
type FileSelnLocal = { fileURI: URI, language: string, content: string }
const stringifyFileSelection = ({ fileURI, language, content }: FileSelnLocal) => {
return `\
${fileURI.fsPath}
${tripleTick[0]}${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
${tripleTick[0]}${language}
${content}
${tripleTick[1]}
`
}
const stringifyCodeSelection = ({ fileURI, selectionStr, range }: CodeSelection) => {
const stringifyCodeSelection = ({ fileURI, language, selectionStr, range }: CodeSelection) => {
return `\
${fileURI.fsPath} (lines ${range.startLineNumber}:${range.endLineNumber})
${tripleTick[0]}${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
${tripleTick[0]}${language}
${selectionStr}
${tripleTick[1]}
`
@ -85,14 +85,20 @@ const stringifyFileSelections = async (fileSelections: FileSelection[], voidFile
}))
return fileSlns.map(sel => stringifyFileSelection(sel)).join('\n')
}
const stringifyCodeSelections = (codeSelections: CodeSelection[]) => {
return codeSelections.map(sel => stringifyCodeSelection(sel)).join('\n') || null
return codeSelections.map(sel => {
stringifyCodeSelection(sel)
}).join('\n') || null
}
const stringifySelectionNames = (currSelns: StagingSelectionItem[] | null): string => {
if (!currSelns) return ''
return currSelns.map(s => `${s.fileURI.fsPath}${s.range ? ` (lines ${s.range.startLineNumber}:${s.range.endLineNumber})` : ''}`).join('\n')
}
export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null) => {
const selnsStr = stringifySelectionNames(currSelns)
@ -103,7 +109,10 @@ export const chat_userMessageContent = async (instructions: string, currSelns: S
return str;
};
export const chat_selectionsString = async (prevSelns: StagingSelectionItem[] | null, currSelns: StagingSelectionItem[] | null, voidFileService: IVoidFileService) => {
export const chat_selectionsString = async (
prevSelns: StagingSelectionItem[] | null, currSelns: StagingSelectionItem[] | null,
voidFileService: IVoidFileService,
) => {
// ADD IN FILES AT TOP
const allSelections = [...currSelns || [], ...prevSelns || []]
@ -158,9 +167,7 @@ Directions:
export const rewriteCode_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
export const rewriteCode_userMessage = ({ originalCode, applyStr, language }: { originalCode: string, applyStr: string, language: string }) => {
return `\
ORIGINAL_FILE
@ -205,44 +212,44 @@ For example, if the user is asking you to "make this variable a better name", ma
- Make sure you give enough context in the code block to apply the changes to the correct location in the code`
export const aiRegex_computeReplacementsForFile_userMessage = async ({ searchClause, replaceClause, fileURI, voidFileService }: { searchClause: string, replaceClause: string, fileURI: URI, modelService: IModelService, voidFileService: IVoidFileService }) => {
// export const aiRegex_computeReplacementsForFile_userMessage = async ({ searchClause, replaceClause, fileURI, voidFileService }: { searchClause: string, replaceClause: string, fileURI: URI, voidFileService: IVoidFileService }) => {
// we may want to do this in batches
const fileSelection: FileSelection = { type: 'File', fileURI, selectionStr: null, range: null, state: { isOpened: false } }
// // we may want to do this in batches
// const fileSelection: FileSelection = { type: 'File', fileURI, selectionStr: null, range: null, state: { isOpened: false } }
const file = await stringifyFileSelections([fileSelection], voidFileService)
// const file = await stringifyFileSelections([fileSelection], voidFileService)
return `\
## FILE
${file}
// return `\
// ## FILE
// ${file}
## SEARCH_CLAUSE
Here is what the user is searching for:
${searchClause}
// ## SEARCH_CLAUSE
// Here is what the user is searching for:
// ${searchClause}
## REPLACE_CLAUSE
Here is what the user wants to replace it with:
${replaceClause}
// ## REPLACE_CLAUSE
// Here is what the user wants to replace it with:
// ${replaceClause}
## INSTRUCTIONS
Please return the changes you want to make to the file in a codeblock, or return "no" if you do not want to make changes.`
}
// ## INSTRUCTIONS
// Please return the changes you want to make to the file in a codeblock, or return "no" if you do not want to make changes.`
// }
// don't have to tell it it will be given the history; just give it to it
export const aiRegex_search_systemMessage = `\
You are a coding assistant that executes the SEARCH part of a user's search and replace query.
// // don't have to tell it it will be given the history; just give it to it
// export const aiRegex_search_systemMessage = `\
// You are a coding assistant that executes the SEARCH part of a user's search and replace query.
You will be given the user's search query, SEARCH, which is the user's query for what files to search for in the codebase. You may also be given the user's REPLACE query for additional context.
// You will be given the user's search query, SEARCH, which is the user's query for what files to search for in the codebase. You may also be given the user's REPLACE query for additional context.
Output
- Regex query
- Files to Include (optional)
- Files to Exclude? (optional)
// Output
// - Regex query
// - Files to Include (optional)
// - Files to Exclude? (optional)
`
// `

View file

@ -396,6 +396,8 @@ export const prepareMessages = ({
const { messages: messages3, separateSystemMessageStr } = prepareMessages_systemMessage({ messages: messages2, aiInstructions, supportsSystemMessage })
const { messages: messages4 } = prepareMessages_tools({ messages: messages3, supportsTools })
const { messages: messages5 } = prepareMessages_noEmptyMessage({ messages: messages4 })
console.log('MESSAGES!!!', JSON.stringify(messages, null, 2))
return {
messages: messages5 as any,
separateSystemMessageStr

View file

@ -7,7 +7,6 @@ import Anthropic from '@anthropic-ai/sdk';
import { Ollama } from 'ollama';
import OpenAI, { ClientOptions } from 'openai';
import { Model as OpenAIModel } from 'openai/resources/models.js';
import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../common/helpers/extractCodeFromResult.js';
import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js';
import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js';
@ -236,6 +235,13 @@ const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage
}
type OpenAIModel = {
id: string;
created: number;
object: 'model';
owned_by: string;
}
const _openaiCompatibleList = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider, providerName }: ListParams_Internal<OpenAIModel>) => {
const onSuccess = ({ models }: { models: OpenAIModel[] }) => {
onSuccess_({ models })