Développement de plugins Tiecode
Documentation destinée au développement de plugins pour l'IDE « Tiecode / QiStudio » (nom du package hôte :
com.tiecode.develop). Le nom officiel de « Tiecode Studio » est QiStudio (nouvelle plateforme open engineering, première application mobile à implémenter le standard OSGi, qui étend l'environnement de développement d'ingénierie sous forme de « micro-composants / plugins »). Sources : Rétro-ingénierie destudio-api-1.0-preview5.jar(com.rapid.api.*) + reconstitution du code source d'une douzaine de plugins réels (go / scala / kotlin / linux / aar / libgdx / exécution sans installation, etc.)
Documentation officielle et dépôts (important)
Dépôts officiels sur Gitee
| Dépôt | Description |
|---|---|
| MobileIPE-Plugin-DevManual | Tutoriel officiel de développement de plugins (micro-composants QiStudio). Contient QiStudio核心体系介绍.md, Rapid框架介绍.md, plugin/扩展点介绍.md, plugin/插件清单配置.md, plugin/注册你的微组件——插件.md |
| TiecodeCompiler-Public | Dépôt de livraison du compilateur Tiecode (version multiplateforme). Contient docs/ (documentation sur l'utilisation du compilateur, la syntaxe, l'encapsulation, l'intégration de plateforme), headers/tiec/ (API C + en-têtes d'encapsulation C++), prebuilt/ (bibliothèques dynamiques cli + android/windows/linux/ohos/wasm), stdlibs/ (bibliothèques de base Android/CXX/Web) |
| QiStudio-PluginSamples | Ensemble d'exemples de plugins officiels (19+ exemples : injection de dépendances, animations éditeur/parenthèses arc-en-ciel, journal flottant, gestionnaire de projet Html, requêtes réseau, extension d'icônes, chaîne d'outils Javac9, coloration syntaxique, thème personnalisé, cycle de vie Application, composants de projet, divers menus, pages d'outils, pages de paramètres, etc.) |
Le nom officiel du jar de l'API est
qistudio-api-<version>.jar(lestudio-api-1.0-preview5.jarrétro-conçu dans cette compétence est le SDK côté hôte). Groupe de discussion QQ : 1016399857.
Références d'implémentation communautaire (GitHub)
Ensemble de codes sources de plugins réels associé à cette compétence (dépôt unique avec plusieurs répertoires, contenant 8 plugins complets et compilables) :
https://github.com/boboyr/tiecode-plugin
| Répertoire | Plugin | Capacités démontrées |
|---|---|---|
go语言工程插件/ | Projet langage Go (console + SDL3) | Double modèle + complétion LSP(gopls) + coloration TextMate + chaîne d'outils + configuration d'exécution + nœud virtuel d'arborescence de fichiers |
结绳安卓工程aar构建插件/ | Construction aar/jar | Réutilisation de la chaîne de compilation hôte + sortie chinoise NameGenerator |
结绳免安装运行插件/ | Exécuteur sans installation | Réutilisation des tâches hôtes + chargement par plugin d'APK non installé |
libgdx游戏编辑器插件/ | Éditeur visuel libgdx | Concepteur + menu de la zone de code de l'éditeur + création de classe de scène |
scala安卓工程插件/ | Projet Android Scala | Chaîne de construction sbt complète + modèle « Projet plugin Tiecode » + plugin de construction/débogage |
结绳Linux工程插件/ | Projet Tiecode Linux-SDL3 | Chaîne d'outils GCC + SDL3 + chaîne de tâches |
外部kotlin文件注解插件/ | Annotation de fichier Kotlin externe | API du compilateur Tiecode : CompilerPlugin + AnnotationProcessor + complétion Kotlin |
BasicLanguages-lang参考/ | Langage de base officiel | Référence de coloration syntaxique csml/json/markdown/xml/properties |
0. Concepts fondamentaux et architecture globale
Tiecode est un IDE hôte modulaire. Les plugins sont distribués sous forme de fichiers .rmx. Un .rmx est essentiellement une APK : AndroidManifest.xml + classes.dex + META-INF/program.json. L'hôte découvre les capacités du plugin via les points d'extension déclarés dans program.json, et non via <application> ou les quatre composants principaux du manifeste.
0.1 Trois packages clés
| Package | Rôle |
|---|---|
com.rapid.api.* | SDK du plugin (principal sujet de ce document). Ingénierie, langage, tâche, structure, page, UI, réseau, etc. |
com.rapid.framework.* | Couche framework : VFS (système de fichiers virtuel), clés Service/Data/Event, conteneur program/plugin |
com.tiecode.compiler.* | Compilateur Tiecode (arbre syntaxique / table des symboles / completer/processor de chaîne d'outils) — côté hôte, le plugin utilise généralement uniquement son sous-paquet toolchain |
0.2 Points d'extension (tableau extension de program.json)
Structure complète 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" }
]
}
Explication du champ minSupport (version minimale de l'hôte requise)
minSupport est un entier indiquant la version minimale de Tiecode requise par le plugin. Les valeurs diffèrent selon la version :
| Version de Tiecode | minSupport | Description |
|---|---|---|
| Tiecode 4.6 | 1 | Pour un plugin 4.6, écrire "minSupport": 1 |
| Tiecode 4.7 | 2 | Pour un plugin 4.7, écrire "minSupport": 2 |
C'est-à-dire : lors du développement d'un plugin pour 4.6,
minSupportdoit être1; pour une mise à niveau vers 4.7 (ou un plugin destiné à 4.7), mettre2. Les règles exactes de liaison avec la version de l'hôte sont à confirmer officiellement ; lors du développement, veuillez remplir selon la version cible de l'hôte.
Ensemble complet des points d'extension confirmés (champ point) :
| Point d'extension | Classe à implémenter/hériter | Rôle | Source du plugin |
|---|---|---|---|
application | AndroidPlugin | Point d'entrée du plugin, onInit/activate/onUninstall | go/scala/aar/sans installation |
project.manager | ProjectManager (couramment CodeProjectManager) | Enregistrer un type de projet | go/scala/linux |
project.language | LanguageFactory | Enregistrer un langage (coloration + extension de fichier associée), peut avoir un scope | go/scala |
project.language.engine | LanguageEngine | Enregistrer un moteur de langage (conteneur de providers comme la complétion, le diagnostic) | Plugin console Tiecode |
project.toolchain | Sous-classe de Tool (ex. SunJdk8Compiler) | Enregistrer une chaîne d'outils de compilation | JavaCompiler8 |
project.component | ComponentIntegrator/ComponentFactory | Monter des composants à l'ouverture du projet (configuration d'exécution/première fenêtre contextuelle/hook) | linux/kotlin/aar/sans installation |
project.file.creationOption | PSObjectCreationOption | Élément de menu « Nouveau fichier » dans l'arborescence | kotlin/libgdx |
project.file.openWithEditor | PSObjectOpeningOption | « Ouvrir avec » pour les fichiers | kotlin |
project.event.fileUi | ProjectFileUiEvent | Événement UI de l'arborescence (lier des icônes, etc.) | kotlin |
domain.menu | BaseDomainMenuAction/action de menu | Élément de menu de premier niveau | go |
domain.menu.editor.code | Menu de la zone de code de l'éditeur | libgdx | |
platform.settingsBuilder | SettingsBuilder | Ajouter une sous-page de paramètres | scala Android |
project.file.creationOption | — | — | — |
Remarque :
scope.identifierssert à limiter un langage/composant à un typeId de projet spécifique (ex.sample.go). La classe de certains points d'extension peut êtreNone(espace réservé).
1. Point d'entrée du plugin : point d'extension 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) {
// Enregistrer une chaîne d'outils de compilation personnalisée (exemple : compilateur Go)
ToolchainService service = Platform.getService(ProjectServiceKeys.TOOLCHAIN);
service.setTool(GoCompileApiKeys.Gocompile, new BaseGoCompiler());
}
@Override
public void activate() {
// Exécuté une fois lors de l'activation du plugin (contient souvent des hooks, des écouteurs, l'assemblage du plugin de compilation)
}
@Override
public void onUninstall() {
// Nettoyage à la désinstallation : terminer ses propres processus enfants (ex. gopls), déconnecter LSP
}
}
API clé (com.rapid.api.Platform) :
Platform.getService(Key<T>)— Récupérer un service hôte (ex.ProjectServiceKeys.TOOLCHAIN)Platform.getProject()— Objet du projet actuellement ouvertPlatform.getApplicationContext()— Contexte hôte
2. Type de projet : point d'extension project.manager
Un plugin enregistre un « type de projet » (ex. projet langage Go, projet Android Tiecode) via ProjectManager.
2.1 Squelette du gestionnaire
public class GoProjectManager extends CodeProjectManager<GoProject> {
public final static String TYPE = "go语言工程"; // Nom affiché
public final static String IDENTIFIER = "sample.go"; // typeId, stocké dans project.json
public GoProjectManager() {
super();
this.fileManager = new GoFileManager(); // Optionnel : gestionnaire de fichiers personnalisé (lier des icônes, etc.)
// ① Ouverture de fichier (par extension)
fileManager.addPSObjectOpeningOption(DefaultOpeningOption.INSTANCE, ".go", ".mod");
// ② Expansion des nœuds de l'arborescence (nœud virtuel « Bibliothèques externes »)
fileManager.addPSObjectExpansionCase(new ExternalLibrariesExpansionCase(), MARKER);
// ③ Élément de menu Nouveau fichier
fileManager.addPSObjectCreationOption(new GoFileCreationOption());
// ④ Moteur de langage et fabrique de langage
languageManager.registerEngine("go", GoLanguageEngine.getInstance());
languageManager.registerLanguageFactory(new GoLanguageFactory());
languageManager.registerOperatorProvider(GoLanguage.ID, new GoOperatorProvider());
// ⑤ Modèles de création de projet
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éthodes de l'interface ProjectManager (d'après javap) :
String getIdentifier();
TypeDescription getTypeDescription(); // Nom + type + icône
List<Template<T>> getTemplates();
void addTemplate(Template<T>);
ProjectFileManager<T> getFileManager();
T newProject();
void load(T, LoadingPresentation);
boolean isProjectDir(File); // Déterminer si un répertoire est de ce type de projet
String backup(T); void delete(T);
Constantes de type pour TypeDescription : APPLICATION / CONSOLE / LIBRARY.
3. Objet projet et assemblage du pipeline : CodeProject.prepare()
Un type de projet correspond à une sous-classe de Project. prepare() est appelé par l'hôte à l'ouverture du projet, à ce moment les fichiers modèles ont déjà été écrits, et on peut assembler la chaîne de tâches, les configurations d'exécution et les composants en fonction du contenu du répertoire.
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); // Dépendance de tâche
addRunConfiguration(new GoSdl3RunConfiguration());
} else {
addTask(new CompilerGoTask());
addTask(new RunGoTask());
addRunConfiguration(new GoStartRunConfig());
addRunConfiguration(new ElfRunConfig());
}
}
@Override public ProjectVariant getVariant() { return ProjectVariant.DEBUG; }
}
Méthodes clés de CodeProject (qui étend BaseProject) :
void prepare();
TaskContainer getTasks(); TaskExecutor getTaskExecutor();
void addTask(Task); void setTasks(Iterable<Task>);
void addRunConfiguration(RunConfiguration); // Propre à CodeProject
ProjectVariant getVariant(); void setVariant(ProjectVariant);
Logger getLogger(); void setLogger(Logger);
String getName();
File getProjectDir(); File getChildFile(String); // Résoudre un chemin relatif en File
void recycle();
Valeurs de ProjectVariant : DEBUG / RELEASE / UNDEFINED.
Astuce (du plugin Go) : dans
prepare(), écrire des fichiers marqueurs (ex.External Libraries) pour ajouter des capacités à un projet existant en une seule fois, sans avoir à le recréer.
4. Modèle de projet : BaseTemplate
Un modèle détermine les options disponibles dans la boîte de dialogue « Nouveau projet » et les fichiers écrits lors de la création.
public class GoConsoleTemplate extends BaseTemplate<GoProject> {
@Override public String getName() { return "控制台工程"; }
@Override public Drawable getPicture() { return null; } // Aperçu du modèle, peut être null
@Override public String getDefaultProjectName() { return "我的go控制台工程"; }
@Override public Form getForm() { return new Form(); } // Peut ajouter des champs de saisie
@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); // Écrire dans le répertoire du projet
FileUtils.writeFile("module main\ngo 1.26.0", project.getChildFile("src/go.mod"));
listener.onSuccess(); // Callback de succès
} catch (IOException e) {
listener.onFail("写出默认文件失败"); // Callback d'échec
}
}
}
Méthodes de l'interface Template<T> :
String getName();
Drawable getPicture();
String getDefaultProjectName();
Form getForm(); // com.rapid.framework.data.form.Form, peut utiliser addInput(new FormInput(...))
void create(T, Form, OnProjectCreateListener); // OnProjectCreateListener.onSuccess()/onFail(msg)
Ressources transportées par le modèle : inclure les fichiers du modèle dans app/src/main/assets/<nom>, utiliser AssetUtils.readAsset / AssetUtils.unZipAssetsFolder(context, zipName, destDir) pour les lire et les décompresser. (Le modèle de projet SDL3 décompresse entièrement gosdl3_template.zip depuis asset vers le répertoire du projet.)
5. Tâche de compilation et chaîne d'outils : BaseTask + ToolchainService
5.1 Tâche 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); }
GoCompileParams p = new GoCompileParams();
p.inputFiles = FileUtils.getGofile(srcDir);
p.outputPath = output.getPath();
p.options = new String[]{"build", "-o"};
a.compile(p, new CompileOnExecutionCallback());
}
// Doit être redéfini : BaseTask.prepare par défaut lance UnsupportedOperationException ;
// De plus, les champs logger/variant doivent être assignés ici, sinon logger.info dans execute() provoquera un NPE.
@Override public void prepare(Project project, ProjectVariant variant, Logger logger) {
this.logger = logger;
this.variant = variant;
Os.setenv("GOROOT", goRoot, false); // Définir la variable d'environnement (android.system.Os)
}
@Override public String getId() { return "compileWithGo"; } // Pour référence par getTaskId() de la configuration d'exécution
@Override public String getName() { return "编译到目标平台"; }
}
Interface Task (d'après javap) :
String getId(); String getName();
void prepare(Project, ProjectVariant, Logger); // Remarque : par défaut, lève une exception, assurez-vous de la redéfinir
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();
Piège (note du plugin Go) : si
loggern'est pas assigné dans la tâche, utiliserlogger.infodansexecute()provoque un NPE et l'installation de la chaîne d'outils ne s'exécute jamais.
5.2 Chaîne d'outils Tool + Installable
public interface Tool { /* Contient les clés intégrées 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;
}
- Les chaînes d'outils personnalisées sont enregistrées dans
application.onInit()avecToolchainService.setTool(customKey, impl). - Les clés des chaînes d'outils Android intégrées se trouvent dans l'interface
Tool(on peut directement utiliserPlatform.getService(TOOLCHAIN).getTool(Tool.AAPT2)). - Réutilisation de la chaîne de compilation hôte (méthode du plugin aar/plugin sans installation) : ne pas empaqueter son propre compilateur, mais utiliser
getDependencyTaskByName("compileWithTiecodec")pour récupérer la tâche hôte et la lier comme dépendance, ou directement réutilisergetTaskId().
6. Configuration d'exécution : RunConfiguration / TaskRunConfiguration
Ajoute des éléments exécutables au menu « Exécuter » du projet. Le plus courant est TaskRunConfiguration : spécifier un id de tâche, et après l'exécution réussie de la tâche, le callback onResult est appelé.
public class GoStartRunConfig extends TaskRunConfiguration {
public Project getProject() { return Platform.getProject(); }
public String getTaskId() { return "RunWithGo"; } // Id de la tâche associée
public ProjectVariant getVariant() { return ProjectVariant.DEBUG; }
@Override
public void onResult(boolean result, Task task) {
if (result) {
// Exécution réussie, lancer le programme
}
}
@Override
public String getConfigurationName() { return "Exécuter Go"; }
@Override
public String getDescription() { return "Compiler et exécuter le programme Go"; }
}
[Le contenu a été tronqué pour la traduction. La compétence continue avec les sections suivantes.]