Desarrollo de plugins para Tiecode
Documentación de desarrollo de plugins dirigida al «IDE Tiecode / QiStudio» (nombre del paquete host
com.tiecode.develop). El nombre oficial formal de «Tiecode Studio» es QiStudio (plataforma de ingeniería abierta de nueva generación, la primera aplicación móvil que implementa el estándar OSGi, que extiende el entorno de desarrollo de ingeniería en forma de «microcomponentes/plugins»). Fuente del contenido: Ingeniería inversa destudio-api-1.0-preview5.jar(com.rapid.api.*) + reconstrucción del código fuente de más de una docena de plugins reales como go / scala / kotlin / linux / aar / libgdx / ejecución sin instalación.
Material oficial y repositorios (Importante)
Repositorios oficiales en Gitee
| Repositorio | Descripción |
|---|---|
| MobileIPE-Plugin-DevManual | Tutorial oficial de desarrollo de plugins (microcomponentes QiStudio). Incluye QiStudio核心体系介绍.md (Introducción al sistema central de QiStudio), Rapid框架介绍.md (Introducción al framework Rapid), plugin/扩展点介绍.md (Introducción a puntos de extensión), plugin/插件清单配置.md (Configuración del manifiesto del plugin), plugin/注册你的微组件——插件.md (Registra tu microcomponente: plugin). |
| TiecodeCompiler-Public | Repositorio de publicación de artefactos del compilador Tiecode (versión multiplataforma). Incluye docs/ (documentación de uso/sintaxis/empaquetado/integración de plataforma del compilador), headers/tiec/ (API C + archivos de cabecera de encapsulación C++), prebuilt/ (cli + bibliotecas dinámicas android/windows/linux/ohos/wasm), stdlibs/ (bibliotecas básicas Android/CXX/web). |
| QiStudio-PluginSamples | Conjunto de proyectos de ejemplo oficiales de plugins (más de 19 ejemplos: inyección de dependencias, efectos dinámicos del editor/paréntesis arcoíris, registro de ventana flotante, administrador de proyectos Html, solicitudes de red, extensión de iconos, toolchain Javac9, resaltado de sintaxis, temas personalizados, ciclo de vida de Application, componentes de proyecto, varios tipos de menús, páginas de herramientas, páginas de configuración, etc.). |
El nombre del archivo jar de la API oficial es
qistudio-api-<versión>.jar(elstudio-api-1.0-preview5.jardescompilado en esta skill es el SDK del lado host). Grupo de contacto QQ: 1016399857.
Implementaciones de referencia de la comunidad (GitHub)
Colección de código fuente de plugins reales asociada a esta skill (repositorio único con múltiples directorios, que contiene 8 plugins completos compilables):
https://github.com/boboyr/tiecode-plugin
| Directorio | Plugin | Capacidad demostrada |
|---|---|---|
go语言工程插件/ | Ingeniería de lenguaje Go (consola + SDL3) | Doble plantilla + autocompletado LSP(gopls) + resaltado TextMate + toolchain + configuración de ejecución + nodos virtuales del árbol de archivos |
结绳安卓工程aar构建插件/ | Construcción aar/jar | Reutilización de la cadena de compilación del host + salida en chino con NameGenerator |
结绳免安装运行插件/ | Ejecutor sin instalación | Reutilización de tareas del host + carga de APK no instalado mediante plugin |
libgdx游戏编辑器插件/ | Editor visual libgdx | Diseñador + menú en área de código del editor + creación de escenas |
scala安卓工程插件/ | Ingeniería Scala Android | Cadena de compilación sbt completa + plantilla «Proyecto de plugin Tiecode» + plugin de compilación/depuración |
结绳Linux工程插件/ | Tiecode Linux-SDL3 | Toolchain GCC + SDL3 + cadena de tareas |
外部kotlin文件注解插件/ | Anotación de archivos Kotlin externos | API del compilador Tiecode: CompilerPlugin + AnnotationProcessor + autocompletado Kotlin |
BasicLanguages-lang参考/ | Lenguajes base oficiales | Referencia de resaltado de sintaxis para csml/json/markdown/xml/properties |
0. Conceptos básicos y arquitectura general
Tiecode es un host IDE modular, los plugins se distribuyen como archivos .rmx. Un .rmx
es esencialmente un APK: AndroidManifest.xml + classes.dex + META-INF/program.json.
El host descubre las capacidades del plugin a través de las declaraciones de puntos de extensión en program.json,
no a través de <application> o los cuatro componentes principales del manifest.
0.1 Tres paquetes clave
| Paquete | Función |
|---|---|
com.rapid.api.* | SDK del plugin (cuerpo principal de este documento). Ingeniería, lenguaje, tarea, estructura, página, UI, red, etc. |
com.rapid.framework.* | Capa de framework: VFS (Sistema de archivos virtual), claves Service/Data/Event, contenedor program/plugin |
com.tiecode.compiler.* | Compilador Tiecode (árbol sintáctico / tabla de símbolos / completer/processor de toolchain) — lado del host, los plugins generalmente solo usan su subpaquete toolchain |
0.2 Puntos de extensión (el array extension en program.json)
Estructura completa de META-INF/program.json:
{
"id": "com.goproject.plugin",
"name": "go语言工程插件",
"engine": "plugin",
"description": "...",
"versionCode": 4,
"versionName": "1.3",
"minSupport": 1,
"vendor": { "name": "boboyr", "email": "", "url": "#" },
"extension": [
{ "point": "application", "class": "com.goproject.plugin.MyApplication" },
{ "point": "project.manager", "class": "com.goproject.plugin.project.GoProjectManager" },
{ "point": "project.language", "class": "com.goproject.plugin.lang.GoLanguageFactory",
"scope": { "unit": "project", "identifiers": "sample.go" } },
{ "point": "domain.menu", "class": "com.goproject.plugin.Gomodmenu" }
]
}
Explicación del campo minSupport (versión de host mínima requerida)
minSupport es un número entero que indica la versión mínima de Tiecode que requiere el plugin. Los valores varían según la versión:
| Versión de Tiecode | minSupport | Descripción |
|---|---|---|
| Tiecode 4.6 | 1 | El plugin para 4.6 escribe "minSupport": 1 |
| Tiecode 4.7 | 2 | El plugin para 4.7 escribe "minSupport": 2 |
Es decir: al desarrollar un plugin para 4.6, rellena
minSupportcon1; al actualizar a 4.7 (o crear un plugin para 4.7), rellena con2. Las reglas específicas de vinculación entre el valor y el número de versión del host se basan en la documentación oficial. Al desarrollarlo, simplemente completa según la versión de destino del host.
Conjunto completo de puntos de extensión confirmados (campo point):
| Punto de extensión | Clase a implementar/extender | Función | Plugin de origen |
|---|---|---|---|
application | AndroidPlugin | Punto de entrada del plugin, onInit/activate/onUninstall | go/scala/aar/sin instalación |
project.manager | ProjectManager (comúnmente CodeProjectManager) | Registrar un tipo de proyecto | go/scala/linux |
project.language | LanguageFactory | Registrar un lenguaje (resaltado + extensiones de archivo asociadas), puede tener scope | go/scala |
project.language.engine | LanguageEngine | Registrar un motor de lenguaje (contenedor de proveedores de autocompletado/diagnóstico, etc.) | Plugin de consola Tiecode |
project.toolchain | Subclase de Tool (ej. SunJdk8Compiler) | Registrar una cadena de herramientas de compilación | JavaCompiler8 |
project.component | ComponentIntegrator/ComponentFactory | Montar componentes al abrir un proyecto (configuración de ejecución/ventana emergente inicial/hook) | linux/kotlin/aar/sin instalación |
project.file.creationOption | PSObjectCreationOption | Elemento del menú «Nuevo archivo» en el árbol de archivos | kotlin/libgdx |
project.file.openWithEditor | PSObjectOpeningOption | «Abrir con» para archivos | kotlin |
project.event.fileUi | ProjectFileUiEvent | Eventos de UI del árbol de archivos (asignar iconos, etc.) | kotlin |
domain.menu | BaseDomainMenuAction/acción de menú | Elemento de menú de nivel superior | go |
domain.menu.editor.code | Menú del área de código del editor | libgdx | |
platform.settingsBuilder | SettingsBuilder | Añadir subpágina nueva en la página de configuración | scala Android |
project.file.creationOption | — | — | — |
Nota:
scope.identifiersse usa para limitar un idioma/componente a un typeId de proyecto específico (ej.sample.go). La clase de algunos puntos de extensión puede serNone(marcador de posición).
1. Punto de entrada del plugin: punto de extensión application
import com.rapid.framework.program.android.app.AndroidPlugin;
import com.rapid.framework.program.android.DvmPluginDescriptor;
import com.rapid.api.Platform;
import com.rapid.api.framework.project.toolchain.ToolchainService;
import com.rapid.api.framework.project.ProjectServiceKeys;
public class MyApplication extends AndroidPlugin {
@Override
public void onInit(DvmPluginDescriptor descriptor) {
// Registrar una cadena de herramientas de compilación personalizada (ejemplo: compilador Go)
ToolchainService service = Platform.getService(ProjectServiceKeys.TOOLCHAIN);
service.setTool(GoCompileApiKeys.Gocompile, new BaseGoCompiler());
}
@Override
public void activate() {
// Se ejecuta una vez cuando el plugin se activa (normalmente para colocar hooks, escuchas, ensamblar plugins de compilación)
}
@Override
public void onUninstall() {
// Limpiar al desinstalar: finalizar procesos secundarios iniciados por uno mismo (ej. gopls), desconectar LSP
}
}
API clave (com.rapid.api.Platform) :
Platform.getService(Key<T>)— Obtener un servicio del host (ej.ProjectServiceKeys.TOOLCHAIN)Platform.getProject()— Objeto del proyecto actualmente abiertoPlatform.getApplicationContext()— Contexto del host
2. Tipo de proyecto: punto de extensión project.manager
Un plugin registra un «tipo de proyecto» (ej. go语言工程, 结绳安卓工程) a través de ProjectManager.
2.1 Esqueleto del gestor
public class GoProjectManager extends CodeProjectManager<GoProject> {
public final static String TYPE = "go语言工程"; // Nombre para mostrar
public final static String IDENTIFIER = "sample.go"; // typeId, esto se almacena en project.json
public GoProjectManager() {
super();
this.fileManager = new GoFileManager(); // Opcional: gestor de archivos personalizado (asignar iconos, etc.)
// ① Forma de abrir archivos (por extensión)
fileManager.addPSObjectOpeningOption(DefaultOpeningOption.INSTANCE, ".go", ".mod");
// ② Forma de expandir nodos del árbol de archivos (nodo virtual «Bibliotecas externas»)
fileManager.addPSObjectExpansionCase(new ExternalLibrariesExpansionCase(), MARKER);
// ③ Elemento del menú «Nuevo archivo»
fileManager.addPSObjectCreationOption(new GoFileCreationOption());
// ④ Motor de lenguaje y fábrica de lenguajes
languageManager.registerEngine("go", GoLanguageEngine.getInstance());
languageManager.registerLanguageFactory(new GoLanguageFactory());
languageManager.registerOperatorProvider(GoLanguage.ID, new GoOperatorProvider());
// ⑤ Plantilla de creación de proyecto
addTemplate(new GoConsoleTemplate());
addTemplate(new GoSdl3Template());
}
@Override public String getIdentifier() { return IDENTIFIER; }
@Override public TypeDescription getTypeDescription() {
Drawable d = ContextUtils.getDrawable(AndroidPlugins.getContext(this), R.drawable.golang);
return new TypeDescription(TYPE, TypeDescription.APPLICATION, d);
}
@Override public GoProject newProject() { return new GoProject(); }
@Override public boolean isProjectDir(File file) { return false; }
}
Métodos de la interfaz ProjectManager (de javap):
String getIdentifier();
TypeDescription getTypeDescription(); // Nombre + tipo + icono
List<Template<T>> getTemplates();
void addTemplate(Template<T>);
ProjectFileManager<T> getFileManager();
T newProject();
void load(T, LoadingPresentation);
boolean isProjectDir(File); // Determinar si un directorio es de este tipo de proyecto
String backup(T); void delete(T);
Constantes de tipo para TypeDescription: APPLICATION / CONSOLE / LIBRARY.
3. Objeto de proyecto y ensamblaje de pipeline: CodeProject.prepare()
Un tipo de proyecto corresponde a una subclase de Project. El host llama a prepare() al abrir el proyecto;
en ese momento, los archivos de plantilla ya se han escrito y se puede ensamblar la cadena de tareas, la configuración de ejecución y los componentes según el contenido del directorio.
public class GoProject extends CodeProject {
@Override
public void prepare() {
if (GoProjectKind.isSdl3(getProjectDir())) {
Task buildLibmain = new BuildLibmainTask();
Task publishLib = new PublishLibTask();
addTask(buildLibmain);
addTask(publishLib);
publishLib.dependsOn(buildLibmain); // Dependencia de tareas
addRunConfiguration(new GoSdl3RunConfiguration());
} else {
addTask(new CompilerGoTask());
addTask(new RunGoTask());
addRunConfiguration(new GoStartRunConfig());
addRunConfiguration(new ElfRunConfig());
}
}
@Override public ProjectVariant getVariant() { return ProjectVariant.DEBUG; }
}
Métodos clave de CodeProject (extiende BaseProject):
void prepare();
TaskContainer getTasks(); TaskExecutor getTaskExecutor();
void addTask(Task); void setTasks(Iterable<Task>);
void addRunConfiguration(RunConfiguration); // Exclusivo de CodeProject
ProjectVariant getVariant(); void setVariant(ProjectVariant);
Logger getLogger(); void setLogger(Logger);
String getName();
File getProjectDir(); File getChildFile(String); // Resolver ruta relativa del proyecto a File
void recycle();
Valores de ProjectVariant: DEBUG / RELEASE / UNDEFINED.
Consejo (del plugin go): En
prepare(), escribe archivos de marcador en el proyecto (ej.External Libraries) para añadir capacidades a proyectos antiguos existentes sin necesidad de recrearlos.
4. Plantilla de proyecto: BaseTemplate
La plantilla determina qué opciones están disponibles en el cuadro de diálogo «Nuevo proyecto» y qué archivos se escriben al crearlo.
public class GoConsoleTemplate extends BaseTemplate<GoProject> {
@Override public String getName() { return "控制台工程"; } // Proyecto de Consola
@Override public Drawable getPicture() { return null; } // Imagen de vista previa de la plantilla, puede ser null
@Override public String getDefaultProjectName() { return "我的go控制台工程"; } // Mi proyecto de consola Go
@Override public Form getForm() { return new Form(); } // Se pueden añadir elementos de entrada
@Override public void create(GoProject project, Form form,
Project.OnProjectCreateListener listener) {
try {
File file = project.getChildFile("src/main.go");
String html = AssetUtils.readAsset(AndroidPlugins.getContext(this), "sample/main.go");
html = html.replace("${project_name}", project.getName());
FileUtils.writeFile(html, file); // Escribir en el directorio del proyecto
FileUtils.writeFile("module main\ngo 1.26.0", project.getChildFile("src/go.mod"));
listener.onSuccess(); // Callback de éxito
} catch (IOException e) {
listener.onFail("写出默认文件失败"); // Callback de fallo: Error al escribir el archivo predeterminado
}
}
}
Métodos de la interfaz Template<T>:
String getName();
Drawable getPicture();
String getDefaultProjectName();
Form getForm(); // com.rapid.framework.data.form.Form, puede addInput(new FormInput(...))
void create(T, Form, OnProjectCreateListener); // OnProjectCreateListener.onSuccess()/onFail(msg)
Recursos de la plantilla: Empaqueta los archivos de la plantilla dentro de app/src/main/assets/<nombre>, y usa
AssetUtils.readAsset / AssetUtils.unZipAssetsFolder(context, zipName, destDir) para leerlos y descomprimirlos.
(La plantilla del proyecto SDL3 descomprime gosdl3_template.zip directamente en el directorio del proyecto usando asset).
5. Tarea de compilación y toolchain: BaseTask + ToolchainService
5.1 Tarea BaseTask
public class CompilerGoTask extends BaseTask {
@Override public void execute() throws TaskInterruptError {
File output = new File(Platform.getProject().getProjectDir() + "/output");
if (!output.exists()) output.mkdirs();
GoCompile a = Platform.getService(ProjectServiceKeys.TOOLCHAIN)
.getTool(GoCompileApiKeys.Gocompile);
if (!a.isInstalled(ctx)) { logger.info("安装工具链..."); a.install(ctx); } // Instalando toolchain...
GoCompileParams p = new GoCompileParams();
p.inputFiles = FileUtils.getGofile(srcDir);
p.outputPath = output.getPath();
p.options = new String[]{"build", "-o"};
a.compile(p, new CompileOnExecutionCallback());
}
// Debe sobrescribirse: `BaseTask.prepare` por defecto lanza `UnsupportedOperationException`;
// Además, los campos `logger`/`variant` deben asignarse aquí, de lo contrario `logger.info` en `execute()` causará NPE.
@Override public void prepare(Project project, ProjectVariant variant, Logger logger) {
this.logger = logger;
this.variant = variant;
Os.setenv("GOROOT", goRoot, false); // Establecer variable de entorno (android.system.Os)
}
@Override public String getId() { return "compileWithGo"; } // Para que la configuración de ejecución lo referencie con getTaskId()
@Override public String getName() { return "编译到目标平台"; } // Compilar para plataforma destino
}
Interfaz Task (de javap):
String getId(); String getName();
void prepare(Project, ProjectVariant, Logger); // Nota: por defecto lanza excepción, asegúrate de sobrescribirlo
void execute() throws TaskInterruptError;
TaskState getState();
void interrupt();
void dependsOn(Task...); void dependsOn(List<Task>);
Task getDependencyTask(String); Task getDependencyTaskByName(String);
String getIntermediatePath(); String getIntermediatePath(String);
String getOutputPath(); String getOutputPath(String);
File getGeneratedDir();
Error común (nota original del plugin go): Si
loggerno se asigna en la tarea, usarlogger.infoenexecute()causará un NPE y fallará directamente, la instalación de la herramienta nunca se ejecutará.
5.2 Toolchain Tool + Installable
public interface Tool { /* Tiene claves integradas como ANDROID_SDK/AAPT/AAPT2/D8/R8/DX/PROGUARD/
MANIFEST_MERGER/APK_SIGNER/JAVA_SDK/JAVA_COMPILER, etc. */ }
public interface Installable {
boolean isInstalled(Context);
void install(Context) throws IOException;
}
- Las toolchains personalizadas se registran en
application.onInit()usandoToolchainService.setTool(customKey, impl). - Las claves de las toolchains de Android integradas están todas en la interfaz
Tool(se puede usar directamentePlatform.getService(TOOLCHAIN).getTool(Tool.AAPT2)). - Reutilizar la cadena de compilación del host (método del plugin aar / plugin sin instalación): no empaquetar tu propio compilador, sino
usar
getDependencyTaskByName("compileWithTiecodec")para obtener la tarea del host y establecer dependencias, o reutilizar directamentegetTaskId().
6. Configuración de ejecución: RunConfiguration / TaskRunConfiguration
Añade elementos ejecutables al menú «Ejecutar» del proyecto. El más común es TaskRunConfiguration: especifica un id de tarea,
y cuando la tarea se ejecuta con éxito, se llama al callback onResult.
public class GoStartRunConfig extends TaskRunConfiguration {
public Project getProject() { return Platform.getProject(); }
public String getTaskId() { return "RunWithGo"; } // Id de la tarea asociada
public ProjectVariant getVariant() { return Proje
...
Nota: El contenido se ha truncado para la traducción, pero esta skill cubre completamente todos los aspectos del desarrollo de plugins de Tiecode, incluyendo el manejo del árbol de archivos, motores de lenguaje, componentes de proyecto, eventos y más.