Skip to content

Commit

Permalink
Merge pull request #1 from fluxlinkage/master
Browse files Browse the repository at this point in the history
初始版本
  • Loading branch information
fluxlinkage authored Aug 9, 2023
2 parents e2f69dc + 9805852 commit e247f9b
Show file tree
Hide file tree
Showing 29 changed files with 1,825 additions and 0 deletions.
65 changes: 65 additions & 0 deletions CMakeLists.txt
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
)

11 changes: 11 additions & 0 deletions CodeGeeX2.json.in
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
}
31 changes: 31 additions & 0 deletions README.md
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. 没有中文翻译。
8 changes: 8 additions & 0 deletions codegeex2.qrc
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>
276 changes: 276 additions & 0 deletions codegeex2client.cpp
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
Loading

0 comments on commit e247f9b

Please sign in to comment.