-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from fluxlinkage/master
初始版本
- Loading branch information
Showing
29 changed files
with
1,825 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
cmake_minimum_required(VERSION 3.10) | ||
|
||
project(CodeGeeX2) | ||
|
||
set(CMAKE_AUTOMOC ON) | ||
set(CMAKE_AUTORCC ON) | ||
set(CMAKE_AUTOUIC ON) | ||
set(CMAKE_CXX_STANDARD 17) | ||
|
||
find_package(QtCreator REQUIRED COMPONENTS Core) | ||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) | ||
set(QtX Qt${QT_VERSION_MAJOR}) | ||
|
||
# Add a CMake option that enables building your plugin with tests. | ||
# You don't want your released plugin binaries to contain tests, | ||
# so make that default to 'NO'. | ||
# Enable tests by passing -DWITH_TESTS=ON to CMake. | ||
option(WITH_TESTS "Builds with tests" NO) | ||
|
||
if(WITH_TESTS) | ||
# Look for QtTest | ||
find_package(${QtX} REQUIRED COMPONENTS Test) | ||
|
||
# Tell CMake functions like add_qtc_plugin about the QtTest component. | ||
set(IMPLICIT_DEPENDS Qt::Test) | ||
|
||
# Enable ctest for auto tests. | ||
enable_testing() | ||
endif() | ||
|
||
add_qtc_plugin(CodeGeeX2 | ||
PLUGIN_DEPENDS | ||
QtCreator::Core | ||
QtCreator::LanguageClient | ||
DEPENDS | ||
${QtX}::Widgets | ||
${QtX}::Network | ||
QtCreator::ExtensionSystem | ||
QtCreator::Utils | ||
QtCreator::ProjectExplorer | ||
QtCreator::TextEditor | ||
SOURCES | ||
codegeex2.qrc | ||
codegeex2client.cpp | ||
codegeex2client.h | ||
codegeex2constants.h | ||
codegeex2hoverhandler.cpp | ||
codegeex2hoverhandler.h | ||
codegeex2icons.h | ||
codegeex2optionspage.cpp | ||
codegeex2optionspage.h | ||
codegeex2plugin.cpp | ||
codegeex2plugin.h | ||
codegeex2projectpanel.cpp | ||
codegeex2projectpanel.h | ||
codegeex2settings.cpp | ||
codegeex2settings.h | ||
codegeex2suggestion.cpp | ||
codegeex2suggestion.h | ||
codegeex2tr.h | ||
requests/getcompletions.h | ||
codegeex2clientinterface.h | ||
codegeex2clientinterface.cpp | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
\"Name\" : \"CodeGeeX2\", | ||
\"Version\" : \"1.0.0\", | ||
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", | ||
\"Vendor\" : \"[email protected]\", | ||
\"Copyright\" : \"(C) 2023\", | ||
\"License\" : \"This plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\", | ||
\"Description\" : \"CodeGeeX2-6B support\", | ||
\"Url\" : \"https://github.com/fluxlinkage/CodeGeeX2-6B-QtCreator-Plugin\", | ||
$$dependencyList | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,33 @@ | ||
# CodeGeeX2-6B-QtCreator-Plugin | ||
支持CodeGeeX2-6B模型的Qt Creator插件 | ||
# 简介 | ||
支持本地或网络部署的[CodeGeeX2-6B](https://github.com/THUDM/CodeGeeX2)模型。 | ||
目前不支持连接到[CodeGeeX官网](https://codegeex.cn/)。 | ||
参考了[Qt Creator源码](https://github.com/qt-creator/qt-creator)中的[Copilot插件](https://github.com/qt-creator/qt-creator/tree/master/src/plugins/copilot)。 | ||
# 安装 | ||
## 二进制安装 | ||
1. 在release中下载和你的Qt Creator版本完全一致的插件(看运气)。 | ||
2. 关闭Qt Creator。 | ||
3. 复制或者移动到Qt Creator的插件目录(大致是一个QtCreator/lib/qtcreator/plugins路径)。 | ||
4. 打开Qt Creator。 | ||
## 编译源代码安装 | ||
### 需求 | ||
需要cmake、Qt开发环境、Qt Creator(确认安装时选中了Plugin Development)。 | ||
Qt Creator建议11.0及以上版本。Qt开发环境的版本需要与Qt Creator一致(Qt Creator里面“帮助”>“About Qt Creator”可以查看)。 | ||
### 步骤 | ||
以Linux为例,假设Qt和Qt Creator分别位于/opt/Qt/6.4.3/gcc_64、/opt/Qt/Tools/QtCreator目录。 | ||
1. mkdir build | ||
2. cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="/opt/Qt/6.4.3/gcc_64;/opt/Qt/Tools/QtCreator" -DCMAKE_INSTALL_PREFIX=/opt/Qt/Tools/QtCreator | ||
3. make | ||
4. sudo make install | ||
# 使用 | ||
首先部署好[CodeGeeX2-6B](https://github.com/THUDM/CodeGeeX2)模型。 | ||
启动CodeGeeX2-6B的Gradio DEMO(基本命令是python ./demo/run_demo.py,具体说明详见CodeGeeX2-6B模型的文档[启动 Gradio DEMO](https://github.com/THUDM/CodeGeeX2#%E5%90%AF%E5%8A%A8-gradio-demo)部分)。 | ||
注意:局域网部署CodeGeeX2-6B请加入“--listen 0.0.0.0”参数。另外,确保防火墙没有拦截服务。 | ||
在Qt Creator中选择“编辑”>“Preference”,找到“CodeGeeX2”项目,设置参数。局域网使用尤其注意修改IP设置。 | ||
设置完成后应该可以使用了。敲一段代码,停顿几秒(和你显卡性能及参数设置有关),会出现提示。 | ||
按“Tab”键接受,按“Ctrl+右”组合键接受一个片段。 | ||
![截图](screenshot.png) | ||
# 已知问题 | ||
1. 所有代码默认为C++,如果用Qt Creator写其他语言代码可能会出现奇怪的提示。 | ||
2. 没有中文翻译。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<RCC> | ||
<qresource prefix="/codegeex2"> | ||
<file>images/settingscategory_codegeex2.png</file> | ||
<file>images/[email protected]</file> | ||
<file>images/codegeex2.png</file> | ||
<file>images/[email protected]</file> | ||
</qresource> | ||
</RCC> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
// Copyright (C) 2023 The Qt Company Ltd. | ||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 | ||
|
||
#include "codegeex2client.h" | ||
#include "codegeex2constants.h" | ||
#include "codegeex2settings.h" | ||
#include "codegeex2suggestion.h" | ||
#include "codegeex2clientinterface.h" | ||
|
||
#include <languageclient/languageclientinterface.h> | ||
#include <languageclient/languageclientmanager.h> | ||
#include <languageclient/languageclientsettings.h> | ||
|
||
#include <coreplugin/actionmanager/actionmanager.h> | ||
#include <coreplugin/editormanager/editormanager.h> | ||
|
||
#include <projectexplorer/projectmanager.h> | ||
|
||
#include <utils/filepath.h> | ||
|
||
#include <texteditor/textdocumentlayout.h> | ||
#include <texteditor/texteditor.h> | ||
|
||
#include <languageserverprotocol/lsptypes.h> | ||
|
||
#include <QTimer> | ||
#include <QToolButton> | ||
|
||
using namespace LanguageServerProtocol; | ||
using namespace TextEditor; | ||
using namespace Utils; | ||
using namespace ProjectExplorer; | ||
using namespace Core; | ||
|
||
namespace CodeGeeX2::Internal { | ||
|
||
//static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath, | ||
// const FilePath &distPath) | ||
//{ | ||
// CommandLine cmd{nodePath, {distPath.toFSPathString()}}; | ||
|
||
// const auto interface = new LanguageClient::StdIOClientInterface; | ||
// interface->setCommandLine(cmd); | ||
// return interface; | ||
//} | ||
|
||
CodeGeeX2Client::CodeGeeX2Client() | ||
: LanguageClient::Client(new CodeGeeX2ClientInterface()) | ||
{ | ||
setName("CodeGeeX2"); | ||
LanguageClient::LanguageFilter langFilter; | ||
|
||
langFilter.filePattern = {"*"}; | ||
|
||
setSupportedLanguage(langFilter); | ||
start(); | ||
|
||
auto openDoc = [this](IDocument *document) { | ||
if (auto *textDocument = qobject_cast<TextDocument *>(document)) | ||
openDocument(textDocument); | ||
}; | ||
|
||
connect(EditorManager::instance(), &EditorManager::documentOpened, this, openDoc); | ||
connect(EditorManager::instance(), | ||
&EditorManager::documentClosed, | ||
this, | ||
[this](IDocument *document) { | ||
if (auto textDocument = qobject_cast<TextDocument *>(document)) | ||
closeDocument(textDocument); | ||
}); | ||
|
||
for (IDocument *doc : DocumentModel::openedDocuments()) | ||
openDoc(doc); | ||
} | ||
|
||
CodeGeeX2Client::~CodeGeeX2Client() | ||
{ | ||
for (IEditor *editor : DocumentModel::editorsForOpenedDocuments()) { | ||
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) | ||
textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler); | ||
} | ||
} | ||
|
||
void CodeGeeX2Client::openDocument(TextDocument *document) | ||
{ | ||
auto project = ProjectManager::projectForFile(document->filePath()); | ||
if (!isEnabled(project)) | ||
return; | ||
|
||
Client::openDocument(document); | ||
connect(document, | ||
&TextDocument::contentsChangedWithPosition, | ||
this, | ||
[this, document](int position, int charsRemoved, int charsAdded) { | ||
Q_UNUSED(charsRemoved) | ||
if (!CodeGeeX2Settings::instance().autoComplete()) | ||
return; | ||
|
||
auto project = ProjectManager::projectForFile(document->filePath()); | ||
if (!isEnabled(project)) | ||
return; | ||
|
||
auto textEditor = BaseTextEditor::currentTextEditor(); | ||
if (!textEditor || textEditor->document() != document) | ||
return; | ||
TextEditorWidget *widget = textEditor->editorWidget(); | ||
if (widget->multiTextCursor().hasMultipleCursors()) | ||
return; | ||
const int cursorPosition = widget->textCursor().position(); | ||
if (cursorPosition < position || cursorPosition > position + charsAdded) | ||
return; | ||
scheduleRequest(widget); | ||
}); | ||
} | ||
|
||
void CodeGeeX2Client::scheduleRequest(TextEditorWidget *editor) | ||
{ | ||
cancelRunningRequest(editor); | ||
|
||
if (!m_scheduledRequests.contains(editor)) { | ||
auto timer = new QTimer(this); | ||
timer->setSingleShot(true); | ||
connect(timer, &QTimer::timeout, this, [this, editor]() { | ||
if (m_scheduledRequests[editor].cursorPosition == editor->textCursor().position()) | ||
requestCompletions(editor); | ||
}); | ||
connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() { | ||
delete m_scheduledRequests.take(editor).timer; | ||
cancelRunningRequest(editor); | ||
}); | ||
connect(editor, &TextEditorWidget::cursorPositionChanged, this, [this, editor] { | ||
cancelRunningRequest(editor); | ||
}); | ||
m_scheduledRequests.insert(editor, {editor->textCursor().position(), timer}); | ||
} else { | ||
m_scheduledRequests[editor].cursorPosition = editor->textCursor().position(); | ||
} | ||
m_scheduledRequests[editor].timer->start(500); | ||
} | ||
|
||
void CodeGeeX2Client::requestCompletions(TextEditorWidget *editor) | ||
{ | ||
auto project = ProjectManager::projectForFile(editor->textDocument()->filePath()); | ||
|
||
if (!isEnabled(project)) | ||
return; | ||
|
||
Utils::MultiTextCursor cursor = editor->multiTextCursor(); | ||
if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible()) | ||
return; | ||
|
||
const Utils::FilePath filePath = editor->textDocument()->filePath(); | ||
GetCompletionRequest request{ | ||
{TextDocumentIdentifier(hostPathToServerUri(filePath)), | ||
documentVersion(filePath), | ||
Position(cursor.mainCursor()),editor->textDocument()->plainText(),editor->position()}}; | ||
request.setResponseCallback([this, editor = QPointer<TextEditorWidget>(editor)]( | ||
const GetCompletionRequest::Response &response) { | ||
QTC_ASSERT(editor, return); | ||
handleCompletions(response, editor); | ||
}); | ||
m_runningRequests[editor] = request; | ||
sendMessage(request); | ||
} | ||
|
||
void CodeGeeX2Client::handleCompletions(const GetCompletionRequest::Response &response, | ||
TextEditorWidget *editor) | ||
{ | ||
if (response.error()) | ||
log(*response.error()); | ||
|
||
int requestPosition = -1; | ||
if (const auto requestParams = m_runningRequests.take(editor).params()) | ||
requestPosition = requestParams->position().toPositionInDocument(editor->document()); | ||
|
||
const Utils::MultiTextCursor cursors = editor->multiTextCursor(); | ||
if (cursors.hasMultipleCursors()) | ||
return; | ||
|
||
if (cursors.hasSelection() || cursors.mainCursor().position() != requestPosition) | ||
return; | ||
|
||
//qInfo("[1] %s",response.toRawData().data()); | ||
if (const std::optional<GetCompletionResponse> result = response.result()) { | ||
//qInfo("[2]%d",result->completions().toListOrEmpty().size()); | ||
auto isValidCompletion = [](const Completion &completion) { | ||
return completion.isValid() && !completion.text().trimmed().isEmpty(); | ||
}; | ||
QList<Completion> completions = Utils::filtered(result->completions().toListOrEmpty(), | ||
isValidCompletion); | ||
//qInfo("[3]%d",completions.size()); | ||
|
||
// remove trailing whitespaces from the end of the completions | ||
for (Completion &completion : completions) { | ||
const LanguageServerProtocol::Range range = completion.range(); | ||
if (range.start().line() != range.end().line()) | ||
continue; // do not remove trailing whitespaces for multi-line replacements | ||
|
||
const QString completionText = completion.text(); | ||
const int end = int(completionText.size()) - 1; // empty strings have been removed above | ||
int delta = 0; | ||
while (delta <= end && completionText[end - delta].isSpace()) | ||
++delta; | ||
|
||
if (delta > 0) | ||
completion.setText(completionText.chopped(delta)); | ||
} | ||
if (completions.isEmpty()) | ||
return; | ||
editor->insertSuggestion( | ||
std::make_unique<CodeGeeX2Suggestion>(completions, editor->document())); | ||
editor->addHoverHandler(&m_hoverHandler); | ||
} | ||
} | ||
|
||
void CodeGeeX2Client::cancelRunningRequest(TextEditor::TextEditorWidget *editor) | ||
{ | ||
auto it = m_runningRequests.find(editor); | ||
if (it == m_runningRequests.end()) | ||
return; | ||
cancelRequest(it->id()); | ||
m_runningRequests.erase(it); | ||
} | ||
|
||
//void CodeGeeX2Client::requestCheckStatus( | ||
// bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback) | ||
//{ | ||
// CheckStatusRequest request{localChecksOnly}; | ||
// request.setResponseCallback(callback); | ||
|
||
// sendMessage(request); | ||
//} | ||
|
||
//void CodeGeeX2Client::requestSignOut( | ||
// std::function<void(const SignOutRequest::Response &response)> callback) | ||
//{ | ||
// SignOutRequest request; | ||
// request.setResponseCallback(callback); | ||
|
||
// sendMessage(request); | ||
//} | ||
|
||
//void CodeGeeX2Client::requestSignInInitiate( | ||
// std::function<void(const SignInInitiateRequest::Response &response)> callback) | ||
//{ | ||
// SignInInitiateRequest request; | ||
// request.setResponseCallback(callback); | ||
|
||
// sendMessage(request); | ||
//} | ||
|
||
//void CodeGeeX2Client::requestSignInConfirm( | ||
// const QString &userCode, | ||
// std::function<void(const SignInConfirmRequest::Response &response)> callback) | ||
//{ | ||
// SignInConfirmRequest request(userCode); | ||
// request.setResponseCallback(callback); | ||
|
||
// sendMessage(request); | ||
//} | ||
|
||
bool CodeGeeX2Client::canOpenProject(Project *project) | ||
{ | ||
return isEnabled(project); | ||
} | ||
|
||
bool CodeGeeX2Client::isEnabled(Project *project) | ||
{ | ||
if (!project) | ||
return CodeGeeX2Settings::instance().enableCodeGeeX2(); | ||
|
||
CodeGeeX2ProjectSettings settings(project); | ||
return settings.isEnabled(); | ||
} | ||
|
||
} // namespace CodeGeeX2::Internal |
Oops, something went wrong.