diff --git a/app/kdevelop.xml b/app/kdevelop.xml index 5df1b5a4c1..d00bc28cc6 100644 --- a/app/kdevelop.xml +++ b/app/kdevelop.xml @@ -1,33 +1,32 @@ KDevelop Project File + Ficheru de proyeutu de KDevelop Fitxer de projecte del KDevelop Fitxer de projecte del KDevelop Soubor projektu KDevelop KDevelop-Projektdatei - Αρχείο έργου KDevelop KDevelop Project File Archivo de proyecto de KDevelop - KDevelopi projektifail Fichiers de projet KDevelop Ficheiro de proxecto de KDevelop File di progetto di KDevelop KDevelop 프로젝트 파일 Projectbestand van KDevelop Pliki projektu KDevelopa Ficheiro de Projecto do KDevelop - Arquivo de projeto do KDevelop + Arquivo de projeto KDevelop Файл проекта KDevelop Súbor projektu KDevelop KDevelop-projektfil KDevelop Proje Dosyası файл проєкту KDevelop KDevelop 工程文件 KDevelop 專案檔 diff --git a/app/org.kde.kdevelop.desktop b/app/org.kde.kdevelop.desktop index 9c48db6673..0e695030b5 100644 --- a/app/org.kde.kdevelop.desktop +++ b/app/org.kde.kdevelop.desktop @@ -1,103 +1,99 @@ [Desktop Entry] Type=Application Exec=kdevelop MimeType= Icon=kdevelop X-DocPath=kdevelop/index.html Actions=OpenSession; Terminal=false StartupWMClass=kdevelop Name=KDevelop Name[ca]=KDevelop Name[ca@valencia]=KDevelop Name[cs]=KDevelop Name[de]=KDevelop -Name[el]=KDevelop Name[en_GB]=KDevelop -Name[eo]=KDevelop Name[es]=KDevelop Name[et]=KDevelop Name[fi]=KDevelop Name[fr]=KDevelop Name[gl]=KDevelop Name[hu]=KDevelop Name[it]=KDevelop Name[nl]=KDevelop Name[pl]=KDevelop Name[pt]=KDevelop Name[pt_BR]=KDevelop Name[ru]=KDevelop Name[sk]=KDevelop Name[sl]=KDevelop Name[sv]=KDevelop Name[tr]=KDevelop Name[uk]=KDevelop Name[x-test]=xxKDevelopxx Name[zh_CN]=KDevelop Name[zh_TW]=KDevelop GenericName=Integrated Development Environment GenericName[ar]=بيئة تطوير متكاملة GenericName[bs]=Integrisano razvojno okruženje GenericName[ca]=Entorn integrat de desenvolupament GenericName[ca@valencia]=Entorn integrat de desenvolupament GenericName[cs]=Integrované Vývojové Prostředí GenericName[da]=Integreret udviklingsmiljø (IDE) GenericName[de]=Integrierte Entwicklungsumgebung GenericName[el]=ολοκληρωμένο περιβάλλον ανάπτυξης GenericName[en_GB]=Integrated Development Environment GenericName[es]=Entorno de desarrollo integrado GenericName[et]=Integreeritud arenduskeskkond GenericName[fi]=Integroitu kehitysympäristö GenericName[fr]=Environnement de Développement Intégré GenericName[ga]=Timpeallacht Chomhtháite Fhorbartha GenericName[gl]=Entorno de desenvolvemento integrado GenericName[hne]=एकीकृत डेवलपमेंट वातावरन GenericName[hu]=Integrált fejlesztői környezet GenericName[it]=Ambiente di sviluppo integrato GenericName[ja]=統合開発環境 GenericName[kk]=Біріктірілген құрастыру ортасы GenericName[km]=Development Environment ដែល​បាន​រួមបញ្ចូល GenericName[lt]=Integruota programavimo aplinka GenericName[lv]=Integrēta izstrādes vide GenericName[nb]=Integrert utviklingsmiljø GenericName[nds]=Programmsmeed GenericName[nl]=Geïntegreerde ontwikkelomgeving GenericName[pl]=Zintegrowane środowisko programistyczne GenericName[pt]=Ambiente de Desenvolvimento Integrado GenericName[pt_BR]=Ambiente Integrado de Desenvolvimento GenericName[ru]=Интегрированная среда разработки GenericName[sk]=Integrované vývojové prostredie GenericName[sl]=Integrirano razvojno okolje GenericName[sv]=Integrerad utvecklingsmiljö GenericName[tr]=Bütünleşik Geliştirme Ortamı GenericName[ug]=يۈرۈشلەشتۈرۈلگەن ئىجادىيەت مۇھىتى GenericName[uk]=Комплексне середовище розробки GenericName[x-test]=xxIntegrated Development Environmentxx GenericName[zh_CN]=集成开发环境 GenericName[zh_TW]=整合開發環境 Categories=Qt;KDE;Development;IDE; [Desktop Action OpenSession] Name=Open a Session Name[ca]=Obre una sessió Name[ca@valencia]=Obri una sessió Name[cs]=Otevřít sezení Name[de]=Öffnet eine Sitzung -Name[el]=Άνοιγμα συνεδρίας Name[en_GB]=Open a Session Name[es]=Abrir una sesión Name[et]=Seansi avamine Name[fr]=Ouvrir une session Name[gl]=Abrir unha sesión Name[it]=Apri una sessione Name[nl]=Een sessie openen Name[pl]=Otwórz sesję Name[pt]=Abrir uma Sessão Name[pt_BR]=Abrir uma sessão Name[sk]=Otvoriť sedenie -Name[sl]=Odpri sejo Name[sv]=Öppna en session Name[uk]=Відкрити сеанс Name[x-test]=xxOpen a Sessionxx Name[zh_CN]=打开会话 Exec=kdevelop --ps diff --git a/app/org.kde.kdevelop_kdev4.desktop b/app/org.kde.kdevelop_kdev4.desktop index 762d4dc145..14a481fd67 100644 --- a/app/org.kde.kdevelop_kdev4.desktop +++ b/app/org.kde.kdevelop_kdev4.desktop @@ -1,76 +1,74 @@ [Desktop Entry] Type=Application Exec=kdevelop -p %u MimeType=application/x-kdevelop; Icon=kdevelop Terminal=false Name=KDevelop Name[ca]=KDevelop Name[ca@valencia]=KDevelop Name[cs]=KDevelop Name[de]=KDevelop -Name[el]=KDevelop Name[en_GB]=KDevelop -Name[eo]=KDevelop Name[es]=KDevelop Name[et]=KDevelop Name[fi]=KDevelop Name[fr]=KDevelop Name[gl]=KDevelop Name[hu]=KDevelop Name[it]=KDevelop Name[nl]=KDevelop Name[pl]=KDevelop Name[pt]=KDevelop Name[pt_BR]=KDevelop Name[ru]=KDevelop Name[sk]=KDevelop Name[sl]=KDevelop Name[sv]=KDevelop Name[tr]=KDevelop Name[uk]=KDevelop Name[x-test]=xxKDevelopxx Name[zh_CN]=KDevelop Name[zh_TW]=KDevelop GenericName=Integrated Development Environment GenericName[ar]=بيئة تطوير متكاملة GenericName[bs]=Integrisano razvojno okruženje GenericName[ca]=Entorn integrat de desenvolupament GenericName[ca@valencia]=Entorn integrat de desenvolupament GenericName[cs]=Integrované Vývojové Prostředí GenericName[da]=Integreret udviklingsmiljø (IDE) GenericName[de]=Integrierte Entwicklungsumgebung GenericName[el]=ολοκληρωμένο περιβάλλον ανάπτυξης GenericName[en_GB]=Integrated Development Environment GenericName[es]=Entorno de desarrollo integrado GenericName[et]=Integreeritud arenduskeskkond GenericName[fi]=Integroitu kehitysympäristö GenericName[fr]=Environnement de Développement Intégré GenericName[ga]=Timpeallacht Chomhtháite Fhorbartha GenericName[gl]=Entorno de desenvolvemento integrado GenericName[hne]=एकीकृत डेवलपमेंट वातावरन GenericName[hu]=Integrált fejlesztői környezet GenericName[it]=Ambiente di sviluppo integrato GenericName[ja]=統合開発環境 GenericName[kk]=Біріктірілген құрастыру ортасы GenericName[km]=Development Environment ដែល​បាន​រួមបញ្ចូល GenericName[lt]=Integruota programavimo aplinka GenericName[lv]=Integrēta izstrādes vide GenericName[nb]=Integrert utviklingsmiljø GenericName[nds]=Programmsmeed GenericName[nl]=Geïntegreerde ontwikkelomgeving GenericName[pl]=Zintegrowane środowisko programistyczne GenericName[pt]=Ambiente de Desenvolvimento Integrado GenericName[pt_BR]=Ambiente Integrado de Desenvolvimento GenericName[ru]=Интегрированная среда разработки GenericName[sk]=Integrované vývojové prostredie GenericName[sl]=Integrirano razvojno okolje GenericName[sv]=Integrerad utvecklingsmiljö GenericName[tr]=Bütünleşik Geliştirme Ortamı GenericName[ug]=يۈرۈشلەشتۈرۈلگەن ئىجادىيەت مۇھىتى GenericName[uk]=Комплексне середовище розробки GenericName[x-test]=xxIntegrated Development Environmentxx GenericName[zh_CN]=集成开发环境 GenericName[zh_TW]=整合開發環境 Categories=Qt;KDE;Development;IDE; NoDisplay=true diff --git a/app/org.kde.kdevelop_ps.desktop b/app/org.kde.kdevelop_ps.desktop index dd472d8c10..4a2e30d352 100644 --- a/app/org.kde.kdevelop_ps.desktop +++ b/app/org.kde.kdevelop_ps.desktop @@ -1,70 +1,69 @@ [Desktop Entry] Type=Application Exec=kdevelop --ps MimeType= Icon=kdevelop Terminal=false Name=KDevelop (Pick Session) Name[ca]=KDevelop (Selecció de sessió) Name[ca@valencia]=KDevelop (Selecció de sessió) Name[cs]=KDevelop (Zvolte sezení) Name[de]=KDevelop (Sitzung auswählen) -Name[el]=KDevelop (Επιλογή συνεδρίας) Name[en_GB]=KDevelop (Pick Session) Name[es]=KDevelop (escoger sesión) Name[et]=KDevelop (seansi valimine) Name[fi]=KDevelop (valitse istunto) Name[fr]=KDevelop (choisissez une session) Name[gl]=KDevelop (escolla a sesión) Name[hu]=KDevelop (munkamenet felvétele) Name[it]=KDevelop (Scegliere la sessione) Name[nl]=KDevelop (pak een sessie) Name[pl]=KDevelop (Wybór sesji) Name[pt]=KDevelop (Seleccionar uma Sessão) Name[pt_BR]=KDevelop (selecionar uma sessão) Name[ru]=KDevelop (с выбором сеанса) Name[sk]=KDevelop (zvoliť sedenie) Name[sl]=KDevelop (izbor seje) Name[sv]=KDevelop (välj session) Name[tr]=KDevelop (Oturum Seçin) Name[uk]=KDevelop (Вибір сеансу) Name[x-test]=xxKDevelop (Pick Session)xx Name[zh_CN]=KDevelop (选择会话) GenericName=Integrated Development Environment (Pick Session to start with) GenericName[ar]=بيئة تطوير متكاملة (انتقِ جلسةً للبدء بها) GenericName[bs]=Integrisano razvojno okruženje (odaberite sesiju za započeti) GenericName[ca]=Entorn integrat de desenvolupament (Seleccioneu una sessió) GenericName[ca@valencia]=Entorn integrat de desenvolupament (Seleccioneu una sessió) GenericName[cs]=Integrované Vývojové Prostředí (Zvolte si relaci, se kterou si přejete začít) GenericName[da]=Integreret udviklingsmiljø (IDE) (vælg session at starte med) GenericName[de]=Integrierte Entwicklungsumgebung (zu startende Sitzung auswählen) GenericName[el]=Ολοκληρωμένο περιβάλλον ανάπτυξης (Επιλογή συνεδρίας για εκκίνηση) GenericName[en_GB]=Integrated Development Environment (Pick Session to start with) GenericName[es]=Entorno de desarrollo integrado (escoger sesión con la que empezar) GenericName[et]=Integreeritud arenduskeskkond (seansi valimine alustamiseks) GenericName[fi]=Integroitu kehitysympäristö (valitse aloitettava istunto) GenericName[fr]=Environnement de Développement Intégré (choix d'une session avec laquelle démarrer) GenericName[ga]=Timpeallacht Chomhtháite Fhorbartha (Roghnaigh Seisiún i dtosach báire) GenericName[gl]=Entorno de desenvolvemento integrado (Escolla unha sesión coa que comezar) GenericName[hu]=Integrált fejlesztői környezet (először munkamenet felvétele) GenericName[it]=Ambiente di sviluppo integrato (Scegliere la sessione con cui iniziare) GenericName[kk]=Біріктірілген құрастыру ортасы (Бастайтын сеансты таңдап алу) GenericName[lt]=Integruota programavimo aplinka (pasirinkite seansą su kuriuo startuosite) GenericName[nb]=Integrert utviklingsmiljø (velg økt å starte med) GenericName[nds]=Programmsmeed (Söök en Törn ut, mit den Du starten wullt) GenericName[nl]=Geïntegreerde ontwikkelomgeving (pak een sessie om mee te beginnen) GenericName[pl]=Zintegrowane środowisko programistyczne (Wybór sesji do rozpoczęcia) GenericName[pt]=Ambiente de Desenvolvimento Integrado (Seleccionar a sessão com que iniciar) GenericName[pt_BR]=Ambiente Integrado de Desenvolvimento (selecione a sessão para início) GenericName[ru]=Интегрированная среда разработки (с выбором начального сеанса) GenericName[sk]=Integrované vývojové prostredie (zvoľte sedenie na spustenie) GenericName[sl]=Integrirano razvojno okolje (izberite sejo za začetek) GenericName[sv]=Integrerad utvecklingsmiljö (välj session att starta) GenericName[tr]=Tümleşik Geliştirme Ortamı (Başlamak için oturum seçin) GenericName[ug]=يۈرۈشلەشتۈرۈلگەن ئىجادىيەت مۇھىتى(Pick Session to start with) GenericName[uk]=Комплексне середовище розробки (виберіть сеанс, з якого слід розпочати роботу) GenericName[x-test]=xxIntegrated Development Environment (Pick Session to start with)xx GenericName[zh_CN]=集成开发环境(选择要启动的会话) GenericName[zh_TW]=整合開發環境(選擇要開始的工作階段) Categories=Qt;KDE;Development;IDE; X-AppStream-Ignore=true diff --git a/app/plasma/applet/package/metadata.desktop b/app/plasma/applet/package/metadata.desktop index cd69f6535a..dbc6fc02d7 100644 --- a/app/plasma/applet/package/metadata.desktop +++ b/app/plasma/applet/package/metadata.desktop @@ -1,91 +1,91 @@ [Desktop Entry] Name=KDevelop Sessions Name[ar]=جلسات مطوّرك Name[bs]=KDevelop sesije Name[ca]=Sessions del KDevelop Name[ca@valencia]=Sessions del KDevelop Name[cs]=relace KDevelop Name[da]=KDevelop-sessioner Name[de]=KDevelop-Sitzungen Name[el]=Συνεδρίες KDevelop Name[en_GB]=KDevelop Sessions Name[es]=Sesiones de KDevelop Name[et]=KDevelopi seansid Name[fi]=KDevelop-istunnot Name[fr]=Sessions KDevelop Name[ga]=Seisiúin KDevelop Name[gl]=Sesións de KDevelop Name[hu]=KDevelop munkamenetek Name[it]=Sessioni KDevelop Name[kk]=KDevelop сеанстары -Name[lt]=KDevelop sesijos +Name[lt]=KDevelop seansas Name[nb]=KDevelop-økter Name[nds]=KDevelop-Törns Name[nl]=KDevelop sessies Name[pl]=Sesje KDevelop Name[pt]=Sessões do KDevelop Name[pt_BR]=Sessões do KDevelop Name[ru]=Сеансы KDevelop Name[sk]=Sedenia KDevelop Name[sl]=Seje za KDevelop Name[sv]=KDevelop-sessioner Name[tr]=KDevelop Oturumları Name[ug]=KDevelop ئەڭگىمەلىرى Name[uk]=Сеанси KDevelop Name[x-test]=xxKDevelop Sessionsxx Name[zh_CN]=KDevelop 会话 Name[zh_TW]=KDevelop 工作階段 Comment=List and launch KDevelop sessions Comment[ar]=اعرض جلسات مطوّرك وأطلقها Comment[bs]=Listanje i pokretanje KDevelop sesija Comment[ca]=Llista i llança sessions del KDevelop Comment[ca@valencia]=Llista i llança sessions del KDevelop Comment[cs]=Vypsat a spustit sezení KDevelop Comment[da]=Oplist og kør KDevelop-sessioner Comment[de]=KDevelop-Sitzungen anzeigen und starten Comment[el]=Καταγραφή και έναρξη συνεδριών KDevelop Comment[en_GB]=List and launch KDevelop sessions Comment[es]=Listar y lanzar sesiones de KDevelop Comment[et]=KDevelopi seansside nimekiri ja käivitamine Comment[fi]=Luettelee ja käynnistää KDevelop-istuntoja Comment[fr]=Liste et lance des sessions KDevelop Comment[ga]=Taispeáin agus tosaigh seisiúin KDevelop Comment[gl]=Lista e inicia sesións de KDevelop Comment[hu]=KDevelop munkamenetek listázása és indítása Comment[it]=Elenca e lancia le sessioni di KDevelop Comment[kk]=KDevelop сеанстарын тізімдеп жегу Comment[nb]=List opp og start KDevelop-økter Comment[nl]=Maak een lijst van en start KDevelop sessies Comment[pl]=Utwórz listę i uruchom sesje KDevelop Comment[pt]=Listar e lançar sessões do KDevelop Comment[pt_BR]=Listar e lançar sessões do KDevelop Comment[ru]=Просмотр списка и запуск сеансов KDevelop Comment[sk]=Vypísať a spustiť sedenia KDevelop Comment[sl]=Seznam in zagon sej za KDevelop Comment[sv]=Lista och starta KDevelop-sessioner Comment[tr]=KDevelop oturumlarını listele ve başlat Comment[uk]=Показ списку і запуск сеансів KDevelop Comment[x-test]=xxList and launch KDevelop sessionsxx Comment[zh_CN]=列出并启动 KDevelop 会话 Comment[zh_TW]=列出並啟動 KDevelop 工作階段 Icon=kdevelop Type=Service X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/kdevelopsessions.qml X-Plasma-DefaultSize=290,340 X-KDE-PluginInfo-Author=Eike Hein X-KDE-PluginInfo-Email=hein@kde.org X-KDE-PluginInfo-Name=kdevelopsessions X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-Category=Utilities X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true #X-Plasma-Requires-FileDialog=Unused #X-Plasma-Requires-LaunchApp=Required #X-Plasma-NotificationArea=true diff --git a/app/plasma/dataengine/plasma-dataengine-kdevelopsessions.desktop b/app/plasma/dataengine/plasma-dataengine-kdevelopsessions.desktop index 62b8f3259b..85ea86d79d 100644 --- a/app/plasma/dataengine/plasma-dataengine-kdevelopsessions.desktop +++ b/app/plasma/dataengine/plasma-dataengine-kdevelopsessions.desktop @@ -1,85 +1,85 @@ [Desktop Entry] Name=KDevelop Sessions Name[ar]=جلسات مطوّرك Name[bs]=KDevelop sesije Name[ca]=Sessions del KDevelop Name[ca@valencia]=Sessions del KDevelop Name[cs]=relace KDevelop Name[da]=KDevelop-sessioner Name[de]=KDevelop-Sitzungen Name[el]=Συνεδρίες KDevelop Name[en_GB]=KDevelop Sessions Name[es]=Sesiones de KDevelop Name[et]=KDevelopi seansid Name[fi]=KDevelop-istunnot Name[fr]=Sessions KDevelop Name[ga]=Seisiúin KDevelop Name[gl]=Sesións de KDevelop Name[hu]=KDevelop munkamenetek Name[it]=Sessioni KDevelop Name[kk]=KDevelop сеанстары -Name[lt]=KDevelop sesijos +Name[lt]=KDevelop seansas Name[nb]=KDevelop-økter Name[nds]=KDevelop-Törns Name[nl]=KDevelop sessies Name[pl]=Sesje KDevelop Name[pt]=Sessões do KDevelop Name[pt_BR]=Sessões do KDevelop Name[ru]=Сеансы KDevelop Name[sk]=Sedenia KDevelop Name[sl]=Seje za KDevelop Name[sv]=KDevelop-sessioner Name[tr]=KDevelop Oturumları Name[ug]=KDevelop ئەڭگىمەلىرى Name[uk]=Сеанси KDevelop Name[x-test]=xxKDevelop Sessionsxx Name[zh_CN]=KDevelop 会话 Name[zh_TW]=KDevelop 工作階段 Comment=List and launch KDevelop sessions Comment[ar]=اعرض جلسات مطوّرك وأطلقها Comment[bs]=Listanje i pokretanje KDevelop sesija Comment[ca]=Llista i llança sessions del KDevelop Comment[ca@valencia]=Llista i llança sessions del KDevelop Comment[cs]=Vypsat a spustit sezení KDevelop Comment[da]=Oplist og kør KDevelop-sessioner Comment[de]=KDevelop-Sitzungen anzeigen und starten Comment[el]=Καταγραφή και έναρξη συνεδριών KDevelop Comment[en_GB]=List and launch KDevelop sessions Comment[es]=Listar y lanzar sesiones de KDevelop Comment[et]=KDevelopi seansside nimekiri ja käivitamine Comment[fi]=Luettelee ja käynnistää KDevelop-istuntoja Comment[fr]=Liste et lance des sessions KDevelop Comment[ga]=Taispeáin agus tosaigh seisiúin KDevelop Comment[gl]=Lista e inicia sesións de KDevelop Comment[hu]=KDevelop munkamenetek listázása és indítása Comment[it]=Elenca e lancia le sessioni di KDevelop Comment[kk]=KDevelop сеанстарын тізімдеп жегу Comment[nb]=List opp og start KDevelop-økter Comment[nl]=Maak een lijst van en start KDevelop sessies Comment[pl]=Utwórz listę i uruchom sesje KDevelop Comment[pt]=Listar e lançar sessões do KDevelop Comment[pt_BR]=Listar e lançar sessões do KDevelop Comment[ru]=Просмотр списка и запуск сеансов KDevelop Comment[sk]=Vypísať a spustiť sedenia KDevelop Comment[sl]=Seznam in zagon sej za KDevelop Comment[sv]=Lista och starta KDevelop-sessioner Comment[tr]=KDevelop oturumlarını listele ve başlat Comment[uk]=Показ списку і запуск сеансів KDevelop Comment[x-test]=xxList and launch KDevelop sessionsxx Comment[zh_CN]=列出并启动 KDevelop 会话 Comment[zh_TW]=列出並啟動 KDevelop 工作階段 Type=Service Icon=kdevelop X-KDE-ServiceTypes=Plasma/DataEngine X-KDE-Library=plasma_engine_kdevelopsessions X-KDE-PluginInfo-Author=Eike Hein X-KDE-PluginInfo-Email=hein@kde.org X-KDE-PluginInfo-Name=org.kde.kdevelopsessions X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-Category=Utiltities X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true diff --git a/app/plasma/runner/kdevelopsessions.desktop b/app/plasma/runner/kdevelopsessions.desktop index eeba6988f4..dfb5a8ec1c 100644 --- a/app/plasma/runner/kdevelopsessions.desktop +++ b/app/plasma/runner/kdevelopsessions.desktop @@ -1,83 +1,83 @@ [Desktop Entry] Name=KDevelop Sessions Name[ar]=جلسات مطوّرك Name[bs]=KDevelop sesije Name[ca]=Sessions del KDevelop Name[ca@valencia]=Sessions del KDevelop Name[cs]=relace KDevelop Name[da]=KDevelop-sessioner Name[de]=KDevelop-Sitzungen Name[el]=Συνεδρίες KDevelop Name[en_GB]=KDevelop Sessions Name[es]=Sesiones de KDevelop Name[et]=KDevelopi seansid Name[fi]=KDevelop-istunnot Name[fr]=Sessions KDevelop Name[ga]=Seisiúin KDevelop Name[gl]=Sesións de KDevelop Name[hu]=KDevelop munkamenetek Name[it]=Sessioni KDevelop Name[kk]=KDevelop сеанстары -Name[lt]=KDevelop sesijos +Name[lt]=KDevelop seansas Name[nb]=KDevelop-økter Name[nds]=KDevelop-Törns Name[nl]=KDevelop sessies Name[pl]=Sesje KDevelop Name[pt]=Sessões do KDevelop Name[pt_BR]=Sessões do KDevelop Name[ru]=Сеансы KDevelop Name[sk]=Sedenia KDevelop Name[sl]=Seje za KDevelop Name[sv]=KDevelop-sessioner Name[tr]=KDevelop Oturumları Name[ug]=KDevelop ئەڭگىمەلىرى Name[uk]=Сеанси KDevelop Name[x-test]=xxKDevelop Sessionsxx Name[zh_CN]=KDevelop 会话 Name[zh_TW]=KDevelop 工作階段 Comment=Matches KDevelop Sessions Comment[ar]=يطابق جلسات مطوّرك Comment[bs]=Usaglašuje KDevelop Sesije Comment[ca]=Concorda amb sessions del KDevelop Comment[ca@valencia]=Concorda amb sessions del KDevelop Comment[da]=Matcher KDevelop-sessioner Comment[de]=Findet KDevelop-Sitzungen Comment[el]=Ταιριάζει με συνεδρίες KDevelop Comment[en_GB]=Matches KDevelop Sessions Comment[es]=Sesiones coincidentes de KDevelop Comment[et]=KDevelopi seansside leidmine Comment[fi]=Täsmää KDevelop-istuntoihin Comment[fr]=Correspond aux sessions KDevelop Comment[ga]=Comhoiriúnach le Seisiúin KDevelop Comment[gl]=Atopa as sesións de KDevelop Comment[hu]=Illeszkedő KDevelop munkamenetek Comment[it]=Sessioni KDevelop corrispondenti Comment[kk]=Сәйкесті KDevelop сеанстарын іздеу Comment[nb]=Treffer KDevelop-økter Comment[nds]=Kiekt na passen KDevelop-Törns Comment[nl]=Komt overeen met KDevelop sessies Comment[pl]=Dopasowuje sesje KDevelop Comment[pt]=Corresponde às Sessões do KDevelop Comment[pt_BR]=Corresponder às sessões do KDevelop Comment[ru]=Выбирает сеансы KDevelop Comment[sk]=Porovnáva sedenia KDevelop Comment[sl]=Se ujema s sejami za KDevelop Comment[sv]=Matchar KDevelop-sessioner Comment[tr]=KDevelop Oturumlarını Eşleştirir Comment[uk]=Пошук відповідних сеансів KDevelop Comment[x-test]=xxMatches KDevelop Sessionsxx Comment[zh_CN]=匹配 KDevelop 会话 Comment[zh_TW]=比對 KDevelop 工作階段 Icon=kdevelop X-KDE-ServiceTypes=Plasma/Runner Type=Service TryExec=kdevelop X-KDE-Library=krunner_kdevelopsessions X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-Name=kdevelopsessions X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-License=LGPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-AdvertiseSingleRunnerQueryMode=true diff --git a/app_templates/c/CMake/cmake_plainc/cmake_plainc.kdevtemplate b/app_templates/c/CMake/cmake_plainc/cmake_plainc.kdevtemplate index 738d3b4411..6d5b57cca3 100644 --- a/app_templates/c/CMake/cmake_plainc/cmake_plainc.kdevtemplate +++ b/app_templates/c/CMake/cmake_plainc/cmake_plainc.kdevtemplate @@ -1,48 +1,46 @@ # KDE Config File [General] Name=CMake C Name[ca]=CMake C Name[ca@valencia]=CMake C Name[cs]=CMake C Name[de]=CMake C -Name[el]=CMake C Name[en_GB]=CMake C Name[es]=CMake C Name[et]=CMake C Name[fr]=CMake C Name[gl]=CMake C Name[it]=CMake C Name[nl]=CMake C Name[pl]=CMake C Name[pt]=C com CMake Name[pt_BR]=CMake C Name[sk]=CMake C Name[sv]=CMake C Name[uk]=CMake C Name[x-test]=xxCMake Cxx Name[zh_CN]=CMake C Comment=Simple CMake-based C application Comment[ca]=Aplicació senzilla en C basada en CMake Comment[ca@valencia]=Aplicació senzilla en C basada en CMake Comment[cs]=Jednoduchá aplikace C založená na CMake Comment[de]=Einfache auf CMake basierende C-Anwendung -Comment[el]=Απλή εφαρμογή C με βάση το CMake Comment[en_GB]=Simple CMake-based C application Comment[es]=Aplicación C sencilla basada en CMake Comment[et]=Lihtne CMake'i põhine C rakendus Comment[fr]=Application C simple utilisant CMake Comment[gl]=Aplicación básica en C baseada en CMake Comment[it]=Semplice applicazione in C basata su CMake Comment[nl]=Eenvoudig op CMake gebaseerd C programma Comment[pl]=Prosty program w C oparty na CMake Comment[pt]=Aplicação simples em C, baseada no CMake Comment[pt_BR]=Simples aplicativo C baseado no CMake Comment[sk]=Jednoduchá C aplikácia založená na CMake Comment[sv]=Enkelt CMake-baserat C-program Comment[uk]=Проста заснована на CMake програма C Comment[x-test]=xxSimple CMake-based C applicationxx Comment[zh_CN]=基于 CMake 的简单 C 应用程序 Category=Standard/Terminal Icon=default-kdevelop ShowFilesAfterGeneration=main.c ValidProjectName=^[a-zA-Z_][a-zA-Z0-9_]+$ diff --git a/app_templates/cpp/CMake/cmake_kdevplugin/cmake_kdevplugin.kdevtemplate b/app_templates/cpp/CMake/cmake_kdevplugin/cmake_kdevplugin.kdevtemplate index c10590b361..ae8f7529b4 100644 --- a/app_templates/cpp/CMake/cmake_kdevplugin/cmake_kdevplugin.kdevtemplate +++ b/app_templates/cpp/CMake/cmake_kdevplugin/cmake_kdevplugin.kdevtemplate @@ -1,50 +1,48 @@ # KDE Config File [General] Name=Simple KDevelop Plugin Name[ca]=Connector senzill del KDevelop Name[ca@valencia]=Connector senzill del KDevelop Name[cs]=Jednoduchý modul pro KDevelop Name[de]=Einfaches KDevelop-Modul -Name[el]=Απλό πρόσθετο του KDevelop Name[en_GB]=Simple KDevelop Plugin Name[es]=Complemento sencillo de KDevelop Name[et]=Lihtne KDevelopi plugin Name[fr]=Module d'extension KDevelop simple Name[gl]=Complemento simple de KDevelop Name[it]=Semplice estensione per KDevelop Name[nl]=Eenvoudige KDevelop-plug-in Name[pl]=Prosta wtyczka do KDevelopa Name[pt]='Plugin' Simples do KDevelop Name[pt_BR]=Plugin simples do KDevelop Name[sk]=Jednoduchý plugin KDevelop Name[sl]=Preprost vstavek za KDevelop Name[sv]=Enkelt KDevelop-insticksprogram Name[tr]=Basit KDevelop Eklentisi Name[uk]=Простий додаток до KDevelop Name[x-test]=xxSimple KDevelop Pluginxx Name[zh_CN]=简单的 KDevelop 插件 Comment=Generate a general KDevelop plugin, using CMake and C++ Comment[ca]=Genera un connector del KDevelop general, usant CMake i C++ Comment[ca@valencia]=Genera un connector del KDevelop general, usant CMake i C++ Comment[de]=erstellt ein allgemeines KDevelop-Modul mit CMake und C++ -Comment[el]=Δημιουργία ενός γενικού πρόσθετου KDevelop, με χρήση CMake και C++ Comment[en_GB]=Generate a general KDevelop plugin, using CMake and C++ Comment[es]=Generar un complemento general para KDevelop que usa CMake y C++ Comment[et]=Üldise KDevelopi plugina genereerimine CMake'i ja C++ abil Comment[fr]=Génère un module d'extension KDevelop simple, reposant sur CMake et C++ Comment[gl]=Xera un complemento xeral de KDevelop, usando CMake e C++. Comment[it]=Genera un'estensione generica per KDevelop usando CMake e C++ Comment[nl]=Genereer een algemene KDevelop-plug-in, met CMake en C++ Comment[pl]=Utwórz ogólną wtyczkę KDevelop przy użyciu CMake i C++ -Comment[pt]=Gera um 'plugin' genérico do KDevelop, usando o CMake e o C++ +Comment[pt]=Gera um 'plugin' geral do KDevelop, usando o CMake e o C++ Comment[pt_BR]=Gera um plugin genérico do KDevelop usando CMake e C++ Comment[sk]=Vygenerovať všeobecný plugin KDevelop s použitím CMake a C++ Comment[sl]=Ustvari splošni vstavek za KDevelop z uporabo CMake in C++ Comment[sv]=Skapa ett generellt KDevelop-insticksprogram, med användning av CMake och C++ Comment[tr]=CMake ve C++ kullanan genel bir KDevelop eklentisi oluştur Comment[uk]=Створити типовий додаток до KDevelop з використанням CMake і C++ Comment[x-test]=xxGenerate a general KDevelop plugin, using CMake and C++xx Comment[zh_CN]=生成一个使用 CMake 和 C++ 的一般的 KDevelop 插件 ShowFilesAfterGeneration=src/%{APPNAMELC}.cpp,README.md Category=KDevelop/Plugin ValidProjectName=^[a-zA-Z_][a-zA-Z0-9_]+$ diff --git a/app_templates/cpp/CMake/cmake_qt5-qml2/cmake_qt5-qml2.kdevtemplate b/app_templates/cpp/CMake/cmake_qt5-qml2/cmake_qt5-qml2.kdevtemplate index 297dc3f80c..bb04451443 100644 --- a/app_templates/cpp/CMake/cmake_qt5-qml2/cmake_qt5-qml2.kdevtemplate +++ b/app_templates/cpp/CMake/cmake_qt5-qml2/cmake_qt5-qml2.kdevtemplate @@ -1,53 +1,51 @@ # KDE Config File [General] Name=QtQuick 2 Application Name[ca]=Aplicació QtQuick 2 Name[ca@valencia]=Aplicació QtQuick 2 Name[cs]=Aplikace QtQuick2 Name[de]=QtQuick2-Anwendung -Name[el]=Εφαρμογή QtQuick 2 Name[en_GB]=QtQuick 2 Application Name[es]=Aplicación QtQuick 2 Name[et]=QtQuick 2 rakendus Name[fr]=Une application QtQuick 2 Name[gl]=Aplicación de QtQuick 2 Name[it]=Applicazione QtQuick 2 Name[nl]=QtQuick 2-toepassing Name[pl]=Program QtQuick 2 Name[pt]=Aplicação em QtQuick 2 Name[pt_BR]=Aplicativo em QtQuick 2 Name[sk]=Aplikácia QtQuick 2 Name[sl]=Program QtQuick 2 Name[sv]=QtQuick 2-program Name[tr]=QtQuick 2 Uygulaması Name[uk]=Програма QtQuick 2 Name[x-test]=xxQtQuick 2 Applicationxx Name[zh_CN]=QtQuick 2 应用程序 Comment=A Qt5 and QtQuick2 basic application using CMake Comment[ca]=Una aplicació bàsica en Qt5 i QtQuick2 usant el CMake Comment[ca@valencia]=Una aplicació bàsica en Qt5 i QtQuick2 usant el CMake Comment[cs]=Základní aplikace Qt5 a QtQuick2 pomocí CMake Comment[de]=Eine einfache Qt5- und QtQuick2-Anwendung basierend auf CMake. -Comment[el]=Μια βασική εφαρμογή Qt5 και QtQuick2 με χρήση CMake Comment[en_GB]=A Qt5 and QtQuick2 basic application using CMake Comment[es]=Una aplicación básica QtQuick2 y Qt5 que usa CMake Comment[et]=Qt5 ja QtQuick2 põhine baasrakendus CMake'i abil Comment[fr]=Une application basique en Qt5 et QtQuick2 en utilisant CMake Comment[gl]=Unha aplicación básica de Qt5 e QtQuick2 usando CMake Comment[it]=Una semplice applicazione Qt5 e QtQuick2 che usa CMake Comment[nl]=Een Qt5 en QtQuick2 basistoepassing met CMake Comment[pl]=Podstawowy program Qt5 i QtQuick2 wykorzystujący CMake. -Comment[pt]=Uma aplicação básica em Qt5 e QtQuick 2, usando o CMake +Comment[pt]=Uma aplicação básica em Qt5 e QtQuick2 que usa o CMake Comment[pt_BR]=Aplicativo básico em Qt5 e QtQuick2 usando CMake Comment[sk]=Základná aplikácia Qt5 a QtQuick2 pomocou CMake Comment[sl]=Osnovni program Qt5 in QtQuick2 z uporabo CMake Comment[sv]=Ett Qt5 och QtQuick 2-baserat program som använder CMake Comment[tr]=CMake kullanan basit Qt5 ve QtQuick2 uygulaması Comment[uk]=Базова програма на основі Qt5 і QtQuick2 з використанням CMake Comment[x-test]=xxA Qt5 and QtQuick2 basic application using CMakexx Comment[zh_CN]=使用 CMake 的 Qt5 和 QtQuick2 基本应用程序 Category=Qt/Graphical Icon=qt5-qml2.png ShowFilesAfterGeneration=src/main.cpp diff --git a/app_templates/cpp/CMake/cmake_qt5guiapp/cmake_qt5guiapp.kdevtemplate b/app_templates/cpp/CMake/cmake_qt5guiapp/cmake_qt5guiapp.kdevtemplate index 36f90e55a7..2c5d259dbe 100644 --- a/app_templates/cpp/CMake/cmake_qt5guiapp/cmake_qt5guiapp.kdevtemplate +++ b/app_templates/cpp/CMake/cmake_qt5guiapp/cmake_qt5guiapp.kdevtemplate @@ -1,56 +1,54 @@ # KDE Config File [General] Name=CMake Qt5 - C++ Name[ca]=CMake Qt5 - C++ Name[ca@valencia]=CMake Qt5 - C++ Name[cs]=CMake Qt5 - C++ Name[de]=CMake Qt5 - C++ -Name[el]=CMake Qt5 - C++ Name[en_GB]=CMake Qt5 - C++ Name[es]=CMake Qt5 - C++ Name[et]=CMake Qt5 - C++ Name[fi]=CMake Qt5 – C++ Name[fr]=CMake Qt5 - C++ Name[gl]=CMake Qt5 - C++ Name[it]=CMake Qt5 - C++ Name[nl]=CMake Qt5 - C++ Name[pl]=CMake Qt5 - C++ -Name[pt]=Qt5 - C++ com QMake +Name[pt]=CMake com Qt5 - C++ Name[pt_BR]=CMake Qt5 - C++ Name[ru]=Qt5-C++ (CMake) Name[sk]=CMake Qt5 - C++ Name[sl]=CMake Qt5 - C++ Name[sv]=CMake Qt5 - C++ Name[tr]=CMake Qt5 - C++ Name[uk]=CMake Qt5 – C++ Name[x-test]=xxCMake Qt5 - C++xx Name[zh_CN]=CMake Qt5 - C++ Comment=Qt5 CMake Gui application. Generate a CMake/Qt5 based GUI application (crossplatform compatible) Comment[ca]=Aplicació IGU de les Qt5 amb CMake. Genera una aplicació IGU basada en CMake/Qt5 (compatible amb multiplataforma) Comment[ca@valencia]=Aplicació IGU de les Qt5 amb CMake. Genera una aplicació IGU basada en CMake/Qt5 (compatible amb multiplataforma) Comment[cs]=Aplikace Qt5 CMake Gui. Generovat aplikaci GUI založenou na CMake/Qt5 (multiplatformní) Comment[de]=Qt5/CMake-GUI-Anwendung. Eine auf CMake und Qt5 basierende GUI-Anwendung (plattformunabhängig) erstellen. -Comment[el]=Qt5 CMake Gui εφαρμογή. Δημιουργία μιας CMake/Qt5 εφαρμογής με γραφικό περιβάλλον (συμβατή με πολλαπλές πλατφόρμες) Comment[en_GB]=Qt5 CMake Gui application. Generate a CMake/Qt5 based GUI application (crossplatform compatible) Comment[es]=Aplicación con interfaz gráfica Qt5 CMake. Genera una aplicación con interfaz gráfica basada en CMake/Qt5 (compatible multiplataforma) Comment[et]=Qt5 GUI rakendus. QMake/Qt5 põhise rakenduse loomine graafilise kasutajaliidesega (ühildub paljude platvormidega) Comment[fi]=Qt5 CMake Gui -sovellus. Generoi CMake/Qt5-perustainen graafinen käyttöliittymäsovellus (toimii useissa käyttöjärjestelmissä) Comment[fr]=Application avec interface graphique CMake pour Qt5. Génère une application avec interface graphique utilisant CMake / Qt5 (compatible multi-plate-forme) Comment[gl]=Aplicación gráfica con CMake para Qt5. Xera unha aplicación con GUI baseada en CMake/Qt5 (compatíbel con multiplataforma) Comment[it]=Applicazione GUI CMake in Qt5. Genera un'applicazione con GUI basata su CMake/Qt5 (multipiattaforma) Comment[nl]=Gui-toepassing van Qt5 CMake. Genereert een op CMake/Qt5 gebaseerd GUI-programma (crossplatform compatibel) Comment[pl]=Program Qt5 CMake Gui. Generuj programy oparte o CMake/Qt5 (zgodne na wielu platformach) -Comment[pt]=Aplicação gráfica do Qt5 com CMake. Gera uma aplicação gráfica baseada no CMake/Qt5 (compatível com várias plataformas) +Comment[pt]=Aplicação Gráfica com CMake para o Qt5. Gera uma aplicação baseada no QMake/Qt5 com uma interface gráfica (compatível com várias plataformas) Comment[pt_BR]=Aplicativo gráfico com CMake para Qt5. Gera um aplicativo de interface em CMake/Qt5 (compatível com várias plataformas) Comment[ru]=Графическое приложение на Qt5 и CMake. Создаёт графическое кроссплатформенное приложение на базе CMake/Qt5. Comment[sk]=Aplikácia Qt5 CMake Gui. Generuje GUI aplikáciu založenú na CMake/Qt5 (cross-platformovo kompatibilné) Comment[sl]=Ustvari program z grafičnim uporabniškim vmesnikom temelječ na CMake/Qt5 (združljiv z več okolji) Comment[sv]=Qt5-program med grafiskt användargränssnitt. Skapar ett CMake/Qt5-baserat program med grafiskt användargränssnitt (kompatibelt mellan plattformar) Comment[tr]=Qt5 CMake Gui uygulaması. CMake/Qt5 tabanlı bir arayüz uygulaması oluştur (çapraz platform uyumlu) Comment[uk]=Програма Qt5 з графічним інтерфейсом і системою збирання CMake. Створити графічну програму, засновану на CMake/Qt5 (сумісну з декількома платформами) Comment[x-test]=xxQt5 CMake Gui application. Generate a CMake/Qt5 based GUI application (crossplatform compatible)xx Comment[zh_CN]=生成基于 CMake/Qt4 的图形应用程序(跨平台兼容) ShowFilesAfterGeneration=src/%{APPNAMELC}.cpp Category=Qt/Graphical Icon=cmake_qt5guiapp.png ValidProjectName=^[a-zA-Z_][a-zA-Z0-9_]+$ diff --git a/app_templates/cpp/QMake/qmake_qt5guiapp/qmake_qt5guiapp.kdevtemplate b/app_templates/cpp/QMake/qmake_qt5guiapp/qmake_qt5guiapp.kdevtemplate index 1f77bf9f35..cda228cd15 100644 --- a/app_templates/cpp/QMake/qmake_qt5guiapp/qmake_qt5guiapp.kdevtemplate +++ b/app_templates/cpp/QMake/qmake_qt5guiapp/qmake_qt5guiapp.kdevtemplate @@ -1,50 +1,48 @@ [General] Name=QMake Qt5 QWidgets GUI Application Name[ca]=Una aplicació IGU per a QMake Qt5 QWidgets Name[ca@valencia]=Una aplicació IGU per a QMake Qt5 QWidgets Name[cs]=Aplikace prostředí QMake Qt5 QWidgets Name[de]=QMake/Qt 5/QWidgets-Anwendung mit grafischer Oberfläche -Name[el]=Εφαρμογή QMake Qt5 QWidgets GUI Name[en_GB]=QMake Qt5 QWidgets GUI Application Name[es]=Aplicación con interfaz gráfica con QWidgets de Qt5 y QMake Name[et]=QMake Qt5 QWidgetsi GUI rakendus Name[fr]=Une application graphique avec Qt5, QMake et QWidgets Name[gl]=Aplicación gráfica con QMake, Qt5 e QWidgets Name[it]=Applicazione con GUI in QWidgets di Qt5 e QMake Name[nl]=QMake Qt5 QWidgets GUI-toepassing Name[pl]=Aplikacja z interfejsem użytkownika Qt5 QWidgets -Name[pt]=Aplicação Gráfica do QWidgets do Qt5 em QMake +Name[pt]=Aplicação Gráfica de QWidgets do Qt5 com QMake Name[pt_BR]=Aplicativo gráfico do Qt5 QWidgets em QMake Name[sk]=QMake Qt5 QWidgets GUI aplikácia Name[sl]=Grafični program QMake Qt5 QWidgets Name[sv]=QMake Qt5 QWidgets-program med grafiskt användargränssnitt Name[tr]=QMake Qt5 QWidgets Grafik Uygulaması Name[uk]=Програма з графічним інтерфейсом на основі QMake, Qt5 та QWidgets Name[x-test]=xxQMake Qt5 QWidgets GUI Applicationxx Name[zh_CN]=QMake Qt5 QWidgets 图形应用程序 Comment=Generate a QMake/Qt5 based GUI application (crossplatform compatible) Comment[ca]=Genera una aplicació IGU basada en QMake/Qt5 (compatible amb multiplataforma) Comment[ca@valencia]=Genera una aplicació IGU basada en QMake/Qt5 (compatible amb multiplataforma) Comment[cs]=Vytvořit aplikaci založenou na QMake/Qt5 (multiplatformní) Comment[de]=Erstellt eine auf QMake und Qt 5 basierende Anwendung mit grafischer Benutzeroberfläche (Plattformunabhängig) -Comment[el]=Δημιουργία μιας QMake/Qt5 εφαρμογής με γραφικό περιβάλλον (συμβατή με πολλαπλές πλατφόρμες) Comment[en_GB]=Generate a QMake/Qt5 based GUI application (crossplatform compatible) Comment[es]=Genera una aplicación con interfaz gráfica basada en QMake/Qt5 (compatible multiplataforma) Comment[et]=QMake/Qt5 põhise rakenduse loomine graafilise kasutajaliidesega (ühildub paljude platvormidega) Comment[fr]=Générer une application graphique fondée sur QMake/Qt5 (compatible multi-plate-forme) Comment[gl]=Xerar unha aplicación gráfica baseada en QMake e Qt5 (multiplataforma) Comment[it]=Genera un'applicazione basata su QMake/Qt5 con interfaccia utente grafica (compatibile multipiattaforma) Comment[nl]=Op QMake/Qt5 gebaseerde GUI-toepassing genereren (crossplatform compatibel) Comment[pl]=Uwórz aplikację opartą na interfejsie użytkownika QMake/Qt5 (zgodne na wielu platformach) -Comment[pt]=Gera uma aplicação gráfica baseada no CMake/Qt5 (compatível com várias plataformas) +Comment[pt]=Gera uma aplicação baseada no QMake/Qt5 com uma interface gráfica (compatível com várias plataformas) Comment[pt_BR]=Gera um aplicativo gráfico com base no QMake/Qt5 (compatível com várias plataformas) Comment[sk]=Generuje GUI aplikáciu založenú na CMake/Qt5 (cross-platformovo kompatibilné) Comment[sl]=Ustvari program z grafičnim uporabniškim vmesnikom temelječ na QMake/Qt5 (združljiv z več okolji) Comment[sv]=Skapa ett QMake/Qt5-baserat program med grafiskt användargränssnitt (kompatibelt mellan plattformar) Comment[tr]=QMake/Qt5 tabanlı bir arayüz uygulaması oluştur (çapraz platform uyumlu) Comment[uk]=Створити програму з графічним інтерфейсом, засновану на QMake/Qt5 (сумісну з декількома платформами) Comment[x-test]=xxGenerate a QMake/Qt5 based GUI application (crossplatform compatible)xx Comment[zh_CN]=生成基于 CMake/Qt4 的图形应用程序(跨平台兼容) Category=Qt/Graphical (QMake-based) ShowFilesAfterGeneration=main.cpp diff --git a/app_templates/cpp/QMake/qt5-qml2/qt5-qml2.kdevtemplate b/app_templates/cpp/QMake/qt5-qml2/qt5-qml2.kdevtemplate index 091468d400..e24ccd242c 100644 --- a/app_templates/cpp/QMake/qt5-qml2/qt5-qml2.kdevtemplate +++ b/app_templates/cpp/QMake/qt5-qml2/qt5-qml2.kdevtemplate @@ -1,53 +1,51 @@ # KDE Config File [General] Name=QtQuick 2 Application Name[ca]=Aplicació QtQuick 2 Name[ca@valencia]=Aplicació QtQuick 2 Name[cs]=Aplikace QtQuick2 Name[de]=QtQuick2-Anwendung -Name[el]=Εφαρμογή QtQuick 2 Name[en_GB]=QtQuick 2 Application Name[es]=Aplicación QtQuick 2 Name[et]=QtQuick 2 rakendus Name[fr]=Une application QtQuick 2 Name[gl]=Aplicación de QtQuick 2 Name[it]=Applicazione QtQuick 2 Name[nl]=QtQuick 2-toepassing Name[pl]=Program QtQuick 2 Name[pt]=Aplicação em QtQuick 2 Name[pt_BR]=Aplicativo em QtQuick 2 Name[sk]=Aplikácia QtQuick 2 Name[sl]=Program QtQuick 2 Name[sv]=QtQuick 2-program Name[tr]=QtQuick 2 Uygulaması Name[uk]=Програма QtQuick 2 Name[x-test]=xxQtQuick 2 Applicationxx Name[zh_CN]=QtQuick 2 应用程序 Comment=A Qt5 and QML2 basic application using QMake Comment[ca]=Una aplicació bàsica en Qt5 i QML2 usant el QMake Comment[ca@valencia]=Una aplicació bàsica en Qt5 i QML2 usant el QMake Comment[cs]=Základní aplikace Qt5 a QML2 pomocí QMake Comment[de]=Eine einfache Qt5- und QML2-Anwendung basierend auf QMake -Comment[el]=Μια βασική εφαρμογή Qt5 και QML2 με χρήση QMake Comment[en_GB]=A Qt5 and QML2 basic application using QMake Comment[es]=Una aplicación básica QML2 y Qt5 que usa QMake Comment[et]=Qt5 ja QML2 põhine baasrakendus CMake'i abil Comment[fr]=Une application basique en Qt5 et QML2 en utilisant QMake Comment[gl]=Unha aplicación básica de Qt5 e QML2 usando QMake Comment[it]=Una semplice applicazione Qt5 e QML2 che usa QMake Comment[nl]=Een Qt5 en QML2 basistoepassing met QMake Comment[pl]=Podstawowy program Qt5 i QML2 wykorzystujący QMake -Comment[pt]=Uma aplicação básica em Qt5 e QML2, usando o QMake +Comment[pt]=Uma aplicação básica em Qt5 e QML2 que usa o QMake Comment[pt_BR]=Aplicativo básico em Qt5 e QML2 usando QMake Comment[sk]=Základná aplikácia Qt5 a QML2 pomocou QMake Comment[sl]=Osnovni program Qt5 in QML2 z uporabo QMake Comment[sv]=Ett Qt5 och QML2-baserat program som använder QMake Comment[tr]=QMake kullanan basit Qt5 ve QML2 uygulaması Comment[uk]=Базова програма мовою QML2 для Qt5 з використанням QMake Comment[x-test]=xxA Qt5 and QML2 basic application using QMakexx Comment[zh_CN]=使用 QMake 的 Qt5 和 QML2 基本应用程序 Category=Qt/Graphical (QMake-based) Icon=qt5-qml2.png ShowFilesAfterGeneration=src/main.cpp diff --git a/app_templates/empty/empty.kdevtemplate b/app_templates/empty/empty.kdevtemplate index 3cbfc59876..83dbe7b241 100644 --- a/app_templates/empty/empty.kdevtemplate +++ b/app_templates/empty/empty.kdevtemplate @@ -1,51 +1,49 @@ [General] Name=Empty Name[ca]=Buit Name[ca@valencia]=Buit Name[cs]=Prázdné Name[de]=Leer -Name[el]=Κενό Name[en_GB]=Empty Name[es]=En blanco Name[et]=Tühi Name[fr]=Vide Name[gl]=Baleiro Name[it]=Vuoto Name[nb]=Tom Name[nl]=Leeg Name[pl]=Pusta -Name[pt]=Vazia +Name[pt]=Vazio Name[pt_BR]=Vazia Name[se]=Guorus Name[sk]=Prázdne Name[sl]=Prazen Name[sv]=Tom Name[tr]=Boş Name[uk]=Порожній Name[x-test]=xxEmptyxx Name[zh_CN]=清空 Comment=A project to fill with your ideas Comment[ca]=Un projecte per omplir amb les vostres idees Comment[ca@valencia]=Un projecte per omplir amb les vostres idees Comment[de]=Ein Projekt, das Sie mit Ihren Ideen füllen -Comment[el]=Ένα έργο υπό διαμόρφωση με βάση τις ιδέες σας Comment[en_GB]=A project to fill with your ideas Comment[es]=Un proyecto para rellenar con sus ideas Comment[et]=Sinu hiilgavaid mõtteid ootav projekt Comment[fr]=Un projet à remplir avec vos idées Comment[gl]=Un proxecto para encher coas súas ideas. Comment[it]=Un progetto da riempire con le tue idee Comment[nl]=Een project om te vullen met uw ideeën Comment[pl]=Projekt do wypełnienia twoimi pomysłami -Comment[pt]=Um projecto a preencher com as suas ideias +Comment[pt]=Um projecto para preencher com as suas ideias Comment[pt_BR]=Um projeto para preencher com as suas ideias Comment[sk]=Projekt na vyplnenie vašimi nápadmi Comment[sl]=Projekt, ki ga zapolnite z lastnimi idejami Comment[sv]=Ett projekt att fylla med dina idéer Comment[tr]=Fikirlerinizle dolduracağınız bir proje Comment[uk]=Проєкт для заповнення вашими ідеями Comment[x-test]=xxA project to fill with your ideasxx Comment[zh_CN]=用以填充你的想法的项目 Category=Standard/Empty Icon=default-kdevelop ValidProjectName=^[a-zA-Z_][a-zA-Z0-9_]+$ diff --git a/cmake/modules/FindClang.cmake b/cmake/modules/FindClang.cmake index dfc236a3e0..7e47fdb2bc 100644 --- a/cmake/modules/FindClang.cmake +++ b/cmake/modules/FindClang.cmake @@ -1,153 +1,155 @@ # Detect Clang libraries # # Defines the following variables: # CLANG_FOUND - True if Clang was found # CLANG_INCLUDE_DIRS - Where to find Clang includes # CLANG_LIBRARY_DIRS - Where to find Clang libraries # CLANG_BUILTIN_DIR - Where to find Clang builtin includes # # CLANG_CLANG_LIB - Libclang C library # # CLANG_CLANGFRONTEND_LIB - Clang Frontend (C++) Library # CLANG_CLANGDRIVER_LIB - Clang Driver (C++) Library # ... # # CLANG_LIBS - All the Clang C++ libraries # # Uses the same include and library paths detected by FindLLVM.cmake # # See https://clang.llvm.org/docs/InternalsManual.html for full list of libraries #============================================================================= # Copyright 2014-2015 Kevin Funk # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= set(KNOWN_VERSIONS 9 8 7 6.0 5.0 4.0 3.9 3.8) foreach(version ${KNOWN_VERSIONS}) if (LLVM_DIR OR (DEFINED Clang_FIND_VERSION AND Clang_FIND_VERSION VERSION_GREATER version)) break() endif () find_package(LLVM ${version}) endforeach() if (${Clang_FIND_REQUIRED}) if(NOT DEFINED Clang_FIND_VERSION) message(SEND_ERROR "Could not find Clang.") else() message("Found version ${Clang_FIND_VERSION}") endif() endif() set(CLANG_FOUND FALSE) if (LLVM_FOUND AND LLVM_LIBRARY_DIRS) macro(FIND_AND_ADD_CLANG_LIB _libname_) string(TOUPPER ${_libname_} _prettylibname_) find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_} HINTS ${LLVM_LIBRARY_DIRS} ${ARGN}) if (CLANG_${_prettylibname_}_LIB) set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_prettylibname_}_LIB}) endif() endmacro(FIND_AND_ADD_CLANG_LIB) FIND_AND_ADD_CLANG_LIB(clangFrontend) # note: On Windows there's 'libclang.dll' instead of 'clang.dll' -> search for 'libclang', too FIND_AND_ADD_CLANG_LIB(clang NAMES clang libclang) # LibClang: high-level C interface FIND_AND_ADD_CLANG_LIB(clangDriver) FIND_AND_ADD_CLANG_LIB(clangCodeGen) FIND_AND_ADD_CLANG_LIB(clangSema) FIND_AND_ADD_CLANG_LIB(clangChecker) FIND_AND_ADD_CLANG_LIB(clangAnalysis) FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) FIND_AND_ADD_CLANG_LIB(clangRewrite) FIND_AND_ADD_CLANG_LIB(clangAST) FIND_AND_ADD_CLANG_LIB(clangParse) FIND_AND_ADD_CLANG_LIB(clangLex) FIND_AND_ADD_CLANG_LIB(clangBasic) FIND_AND_ADD_CLANG_LIB(clangARCMigrate) FIND_AND_ADD_CLANG_LIB(clangEdit) FIND_AND_ADD_CLANG_LIB(clangFrontendTool) FIND_AND_ADD_CLANG_LIB(clangSerialization) FIND_AND_ADD_CLANG_LIB(clangTooling) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) FIND_AND_ADD_CLANG_LIB(clangRewriteCore) endif() if(CLANG_LIBS OR CLANG_CLANG_LIB) set(CLANG_FOUND TRUE) else() message(STATUS "Could not find any Clang libraries in ${LLVM_LIBRARY_DIRS}") endif() if(CLANG_FOUND) set(CLANG_LIBRARY_DIRS ${LLVM_LIBRARY_DIRS}) set(CLANG_INCLUDE_DIRS ${LLVM_INCLUDE_DIRS}) set(CLANG_VERSION ${LLVM_VERSION}) # svn version of clang has a svn suffix "8.0.0svn" but installs the header in "8.0.0", without the suffix string(REPLACE "svn" "" CLANG_VERSION_CLEAN "${CLANG_VERSION}") + # dito for git + string(REPLACE "git" "" CLANG_VERSION_CLEAN "${CLANG_VERSION}") find_path(CLANG_BUILTIN_DIR # cpuid.h because it is defined in ClangSupport constructor as valid clang builtin dir indicator NAMES "cpuid.h" PATHS "${CLANG_LIBRARY_DIRS}" "${CLANG_INCLUDE_DIRS}" PATH_SUFFIXES "clang/${CLANG_VERSION}/include" "../../../clang/${CLANG_VERSION}/include" "clang/${CLANG_VERSION_CLEAN}/include" "../../../clang/${CLANG_VERSION_CLEAN}/include" NO_DEFAULT_PATH ) if (NOT CLANG_BUILTIN_DIR) message(FATAL_ERROR "Could not find Clang builtin directory") endif() get_filename_component(CLANG_BUILTIN_DIR ${CLANG_BUILTIN_DIR} ABSOLUTE) # check whether llvm-config comes from an install prefix execute_process( COMMAND ${LLVM_CONFIG_EXECUTABLE} --src-root OUTPUT_VARIABLE _llvmSourceRoot OUTPUT_STRIP_TRAILING_WHITESPACE ) string(FIND "${LLVM_INCLUDE_DIRS}" "${_llvmSourceRoot}" _llvmIsInstalled) if (NOT _llvmIsInstalled) message(STATUS "Detected that llvm-config comes from a build-tree, adding more include directories for Clang") list(APPEND CLANG_INCLUDE_DIRS "${LLVM_INSTALL_PREFIX}/tools/clang/include" # build dir ) # check whether the source is from llvm-project.git (currently recommended way to clone the LLVM projects) # contains all LLVM projects in the top-level directory get_filename_component(_llvmProjectClangIncludeDir ${_llvmSourceRoot}/../clang/include REALPATH) if (EXISTS ${_llvmProjectClangIncludeDir}) message(STATUS " Note: llvm-project.git structure detected, using different include path pointing into source dir") list(APPEND CLANG_INCLUDE_DIRS "${_llvmProjectClangIncludeDir}") # source dir else() list(APPEND CLANG_INCLUDE_DIRS "${_llvmSourceRoot}/tools/clang/include") # source dir endif() endif() message(STATUS "Found Clang (LLVM version: ${CLANG_VERSION})") message(STATUS " Include dirs: ${CLANG_INCLUDE_DIRS}") message(STATUS " Clang libraries: ${CLANG_LIBS}") message(STATUS " Libclang C library: ${CLANG_CLANG_LIB}") message(STATUS " Builtin include dir: ${CLANG_BUILTIN_DIR}") else() if(Clang_FIND_REQUIRED) message(FATAL_ERROR "Could NOT find Clang") endif() endif() diff --git a/file_templates/classes/qabstractitemmodel/qabstractitemmodel.desktop b/file_templates/classes/qabstractitemmodel/qabstractitemmodel.desktop index 969423742e..0f8d483365 100644 --- a/file_templates/classes/qabstractitemmodel/qabstractitemmodel.desktop +++ b/file_templates/classes/qabstractitemmodel/qabstractitemmodel.desktop @@ -1,158 +1,154 @@ [General] Name=QAbstractItemModel subclass Name[ca]=Subclasse QAbstractItemModel Name[ca@valencia]=Subclasse QAbstractItemModel Name[cs]=Podtřída QAbstractItemModel Name[de]=QAbstractItemModel-Unterklasse -Name[el]=QAbstractItemModel subclass Name[en_GB]=QAbstractItemModel subclass Name[es]=Subclase de QAbstractItemModel Name[et]=QAbstractItemModeli alamklass Name[fr]=Sous-classe QAbstractItemModel Name[gl]=Subclase de QAbstractItemModel Name[it]=Sottoclasse di QAbstractItemModel Name[nl]=QAbstractItemModel subclass Name[pl]=Podklasa QAbstractItemModel Name[pt]=Sub-classe de QAbstractItemModel Name[pt_BR]=Subclasse de QAbstractItemModel -Name[sk]=Podtrieda QAbstractItemModel -Name[sl]=Pod-razred QAbstractItemModel +Name[sk]=Podtrieda QAbstractItemModel Name[sv]=QAbstractItemModel-delklass Name[uk]=Підклас QAbstractItemModel Name[x-test]=xxQAbstractItemModel subclassxx Name[zh_CN]=QAbstractItemModel 子类 Comment=QAbstractItemModel subclass with properties Comment[ca]=Subclasse QAbstractItemModel amb propietats Comment[ca@valencia]=Subclasse QAbstractItemModel amb propietats Comment[de]=Eine QAbstractItemModel-Unterklasse mit Properties -Comment[el]=QAbstractItemModel subclass με ιδιότητες Comment[en_GB]=QAbstractItemModel subclass with properties Comment[es]=Subclase de QAbstractItemModel con propiedades Comment[et]=QAbstractItemModeli alamklass omadustega Comment[fr]=Sous-classe QAbstractItemModel avec des propriétés Comment[gl]=Subclase de QAbstractItemModel con propiedades Comment[it]=Sottoclasse di QAbstractItemModel con delle proprietà Comment[nl]=QAbstractItemModel subclass met eigenschappen Comment[pl]=Podklasa QAbstractItemModel z właściwościami Comment[pt]=Sub-classe de QAbstractItemModel com propriedades Comment[pt_BR]=Subclasse de QAbstractItemModel com propriedades -Comment[sk]=Podtrieda QAbstractItemModel s vlastnosťami -Comment[sl]=Pod-razred QAbstractItemModel z lastnostmi +Comment[sk]=Podtrieda QAbstractItemModel s vlastnosťami Comment[sv]=QAbstractItemModel-delklass med egenskaper Comment[uk]=Підклас QAbstractItemModel із властивостями Comment[x-test]=xxQAbstractItemModel subclass with propertiesxx Comment[zh_CN]=带属性的 QAbstractItemModel 子类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QAbstractItemModel Files=Header,Implementation OptionsFile=options.kcfg [Header] Name=Public Header Name[bs]=Javno zaglavlje Name[ca]=Capçalera pública Name[ca@valencia]=Capçalera pública Name[da]=Offentlig header Name[de]=Public Header Name[el]=Public Header Name[en_GB]=Public Header Name[es]=Cabecera pública Name[et]=Avalik päis Name[fi]=Julkinen otsikkotiedosto Name[fr]=En-tête public Name[gl]=Cabeceira pública Name[hu]=Publikus fejléc Name[it]=Intestazione pubblica Name[kk]=Ашық (public) айдар Name[nb]=Public hode Name[nl]=Publieke kop Name[pl]=Publiczny Nagłówek Name[pt]=Inclusão Pública Name[pt_BR]=Inclusão pública Name[ru]=Открытый заголовок Name[sk]=Verejná hlavička Name[sl]=Javna glava Name[sv]=Public-deklaration Name[tr]=Genel Başlık Name[uk]=Відкритий (public) заголовок Name[x-test]=xxPublic Headerxx Name[zh_CN]=公有头文件 Name[zh_TW]=公開標頭 File=class.h OutputFile={{ name }}.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp diff --git a/file_templates/classes/qabstractitemmodel_pimpl/qabstractitemmodel_pimpl.desktop b/file_templates/classes/qabstractitemmodel_pimpl/qabstractitemmodel_pimpl.desktop index 27b48117ca..e19f6ad280 100644 --- a/file_templates/classes/qabstractitemmodel_pimpl/qabstractitemmodel_pimpl.desktop +++ b/file_templates/classes/qabstractitemmodel_pimpl/qabstractitemmodel_pimpl.desktop @@ -1,193 +1,189 @@ [General] Name=QAbstractItemModel pimpl subclass Name[ca]=Subclasse QAbstractItemModel «pimpl» Name[ca@valencia]=Subclasse QAbstractItemModel «pimpl» Name[de]=QAbstractItemModel-Unterklasse (pimpl) -Name[el]=AbstractItemModel pimpl subclass Name[en_GB]=QAbstractItemModel pimpl subclass Name[es]=Subclase «pimpl» de QAbstractItemModel Name[et]=QAbstractItemModel pimpl alamklass Name[fr]=Sous-classe QAbstractItemModel pimpl Name[gl]=Subclase de QAbstractItemModel con punteiro opaco Name[it]=Sottoclasse pimpl di QAbstractItemModel Name[nl]=QAbstractItemModel pimpl subclass Name[pl]=Podklasa QAbstractItemModel pimpl Name[pt]=Sub-classe 'pimpl' de QAbstractItemModel Name[pt_BR]=Subclasse pimpl de QAbstractItemModel Name[sk]=Podtrieda QAbstractItemModel pimpl -Name[sl]=Pod-razred pimpl za QAbstractItemModel Name[sv]=QAbstractItemModel pimpl-delklass Name[uk]=Підклас pimpl QAbstractItemModel Name[x-test]=xxQAbstractItemModel pimpl subclassxx Name[zh_CN]=QAbstractItemModel pimpl 子类 Comment=QAbstractItemModel subclass with private implementation Comment[ca]=Una subclasse QAbstractItemModel amb implementació privada Comment[ca@valencia]=Una subclasse QAbstractItemModel amb implementació privada Comment[de]=Eine QAbstractItemModel-Unterklasse mit privater Implementierung -Comment[el]=QAbstractItemModel subclass με private implementation Comment[en_GB]=QAbstractItemModel subclass with private implementation Comment[es]=Una subclase de QAbstractItemModel con una implementación privada Comment[et]=QAbstractItemModeli alamklass privaatse teostusega Comment[fr]=Sous-classe QAbstractItemModel avec une implémentation privée Comment[gl]=Subclase de QAbstractItemModel cunha codificación privada. Comment[it]=Una sottoclasse di QAbstractItemModel con un'implementazione privata Comment[nl]=QAbstractItemModel subclass met privé implementatie Comment[pl]=Podklasa QAbstractItemModel z prywatną implementacją -Comment[pt]=Sub-classe de QAbstractItemModel com uma implementação privada +Comment[pt]=Uma sub-classe de QAbstractItemModel com uma implementação privada Comment[pt_BR]=Subclasse de QAbstractItemModel com uma implementação privada Comment[sk]=Podtrieda QAbstractItemModel so súkromnou implementáciou -Comment[sl]=Pod-razred QAbstractItemModel z zasebno izvedbo Comment[sv]=QAbstractItemModel-delklass med privat implementering Comment[uk]=Підклас QAbstractItemModel із закритою (private) реалізацією Comment[x-test]=xxQAbstractItemModel subclass with private implementationxx Comment[zh_CN]=有私有实现的 QAbstractItemModel 子类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QAbstractItemModel Files=Header,PrivateHeader,Implementation OptionsFile=options.kcfg [Header] Name=Header Name[bs]=Zaglavlje Name[ca]=Capçalera Name[ca@valencia]=Capçalera Name[cs]=Hlavička Name[da]=Header Name[de]=Header Name[el]=Header Name[en_GB]=Header Name[es]=Cabecera Name[et]=Päis Name[fi]=Otsikkotiedosto Name[fr]=En-tête Name[gl]=Cabeceira Name[hu]=Fejléc Name[it]=Intestazione Name[kk]=Айдар Name[mr]=हेडर Name[nb]=Hode Name[nl]=Kop Name[pl]=Nagłówek Name[pt]=Inclusão Name[pt_BR]=Cabeçalho Name[ru]=Заголовок Name[sk]=Hlavička Name[sl]=Glava Name[sv]=Deklaration Name[tr]=Başlık Name[ug]=بەت قېشى Name[uk]=Заголовок Name[x-test]=xxHeaderxx Name[zh_CN]=报头 Name[zh_TW]=標頭 File=class.h OutputFile={{ name }}.h [PrivateHeader] Name=Private Header Name[bs]=Privatno zaglavlje Name[ca]=Capçalera privada Name[ca@valencia]=Capçalera privada Name[da]=Privat header Name[de]=Privater Header Name[el]=Private Header Name[en_GB]=Private Header Name[es]=Cabecera privada Name[et]=Privaatpäis Name[fi]=Yksityinen otsikkotiedosto Name[fr]=En-tête privé Name[gl]=Cabeceira privada Name[hu]=Privát fejléc Name[it]=Intestazione privata Name[kk]=Сырлы (private) айдар Name[nb]=Private hode Name[nl]=Private kop Name[pl]=Prywatny Nagłówek Name[pt]=Inclusão Privada Name[pt_BR]=Inclusão privada Name[ru]=Закрытый заголовок Name[sk]=Súkromná hlavička Name[sl]=Zasebna glava Name[sv]=Privat deklaration Name[tr]=Özel Başlık Name[uk]=Закритий (private) заголовок Name[x-test]=xxPrivate Headerxx Name[zh_CN]=私有头文件 Name[zh_TW]=私密標頭 File=class_p.h OutputFile={{ name }}_p.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp diff --git a/file_templates/classes/qdialog/qdialog.desktop b/file_templates/classes/qdialog/qdialog.desktop index ac75a2bf6a..f6a0693acf 100644 --- a/file_templates/classes/qdialog/qdialog.desktop +++ b/file_templates/classes/qdialog/qdialog.desktop @@ -1,198 +1,194 @@ [General] Name=Dialog with a UI File Name[ca]=Diàleg amb un fitxer IU Name[ca@valencia]=Diàleg amb un fitxer IU Name[de]=Dialog mit einer UI-Datei -Name[el]=Διάλογος με UI αρχείο Name[en_GB]=Dialogue with a UI File Name[es]=Diálogo con un archivo de interfaz gráfica Name[et]=Dialoog UI-failiga Name[fr]=Dialogue avec un fichier d'interface utilisateur Name[gl]=Dialogo cun ficheiro de interface de usuario Name[it]=Finestra di dialogo con un file UI Name[nl]=Dialoog met een UI-bestand Name[pl]=Okno dialogowe z plikiem UI Name[pt]=Janela com um Ficheiro UI Name[pt_BR]=Caixa de diálogo com um arquivo UI -Name[sk]=Dialógové okno s UI súborom -Name[sl]=Pogovorno okno z datoteko uporabniškega vmesnika +Name[sk]=Dialógové okno so súborom UI Name[sv]=Dialogruta med en UI-fil Name[uk]=Діалогове вікно із файлом UI Name[x-test]=xxDialog with a UI Filexx Name[zh_CN]=带 UI 文件的对话框 Comment=QDialog subclass with a separate Qt Designer file Comment[ca]=Subclasse QDialog amb un fitxer separat del Qt Designer Comment[ca@valencia]=Subclasse QDialog amb un fitxer separat del Qt Designer Comment[de]=QDialog-Subklasse mit einer separaten Qt-Designer-Datei -Comment[el]=QDialog subclass με χωριστό Qt Designer αρχείο Comment[en_GB]=QDialog subclass with a separate Qt Designer file Comment[es]=Subclase de QDialog con un archivo separado del Diseñador de Qt Comment[et]=QDialogi alamklass eraldi Qt Designeri failiga Comment[fr]=Sous-classe QDialog avec un fichier Qt Designer séparé Comment[gl]=Subclase de QDialog cun ficheiro separado para Qt Designer Comment[it]=Sottoclasse di QDialog con un file separato di Qt Designer Comment[nl]=QDialog subclass met een apart Qt Designer bestand Comment[pl]=Podklasa QDialog z osobnym plikiem Projektanta Qt Comment[pt]=Sub-classe de QDialog com um ficheiro separado do Qt Designer Comment[pt_BR]=Subclasse de QDialog com um arquivo separado do Qt Designer Comment[sk]=Podtrieda QDialog so samostatným súborom Qt Designer -Comment[sl]=Pod-razred QDialog z ločeno datoteko Qt Designer-ja Comment[sv]=QDialog-delklass med en separat Qt Designer-fil Comment[uk]=Підклас QDialog із окремим файлом Qt Designer Comment[x-test]=xxQDialog subclass with a separate Qt Designer filexx Comment[zh_CN]=带单独 Qt Designer 文件的 QDialog 子类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QDialog Files=Header,Implementation,UI OptionsFile=options.kcfg [Header] Name=Public Header Name[bs]=Javno zaglavlje Name[ca]=Capçalera pública Name[ca@valencia]=Capçalera pública Name[da]=Offentlig header Name[de]=Public Header Name[el]=Public Header Name[en_GB]=Public Header Name[es]=Cabecera pública Name[et]=Avalik päis Name[fi]=Julkinen otsikkotiedosto Name[fr]=En-tête public Name[gl]=Cabeceira pública Name[hu]=Publikus fejléc Name[it]=Intestazione pubblica Name[kk]=Ашық (public) айдар Name[nb]=Public hode Name[nl]=Publieke kop Name[pl]=Publiczny Nagłówek Name[pt]=Inclusão Pública Name[pt_BR]=Inclusão pública Name[ru]=Открытый заголовок Name[sk]=Verejná hlavička Name[sl]=Javna glava Name[sv]=Public-deklaration Name[tr]=Genel Başlık Name[uk]=Відкритий (public) заголовок Name[x-test]=xxPublic Headerxx Name[zh_CN]=公有头文件 Name[zh_TW]=公開標頭 File=class.h OutputFile={{ name }}.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp [UI] Name=User Interface Name[bg]=Потребителски интерфейс Name[bs]=Korisnički interfejs Name[ca]=Interfície d'usuari Name[ca@valencia]=Interfície d'usuari Name[cs]=Uživatelské rozhraní Name[da]=Brugerflade Name[de]=Benutzeroberfläche Name[el]=Περιβάλλον χρήστη Name[en_GB]=User Interface Name[es]=Interfaz de usuario Name[et]=Kasutajaliides Name[fi]=Käyttöliittymä Name[fr]=Interface utilisateur Name[gl]=Interface de usuario Name[hu]=Felhasználói felület Name[it]=Interfaccia utente Name[kk]=Пайдаланушы интерфейсі Name[lv]=Lietotāja saskarne Name[mr]=वापरकर्ता संवाद Name[nb]=Brukerflate Name[nds]=Böversiet Name[nl]=Gebruikersinterface Name[pa]=ਯੂਜ਼ਰ ਇੰਟਰਫੇਸ Name[pl]=Interfejs użytkownika Name[pt]=Interface do Utilizador Name[pt_BR]=Interface do usuário Name[ro]=Interfață utilizator Name[ru]=Пользовательский интерфейс Name[sk]=Užívateľské rozhranie Name[sl]=Uporabniški vmesnik Name[sv]=Användargränssnitt Name[tr]=Kullanıcı arayüzü Name[ug]=كۆرۈنمەيۈز Name[uk]=Інтерфейс користувача Name[x-test]=xxUser Interfacexx Name[zh_CN]=用户界面 Name[zh_TW]=使用者介面 File=class.ui OutputFile={{ name }}.ui diff --git a/file_templates/classes/qdialog_pimpl/qdialog_pimpl.desktop b/file_templates/classes/qdialog_pimpl/qdialog_pimpl.desktop index ddc0a99995..0bb60311d0 100644 --- a/file_templates/classes/qdialog_pimpl/qdialog_pimpl.desktop +++ b/file_templates/classes/qdialog_pimpl/qdialog_pimpl.desktop @@ -1,234 +1,232 @@ [General] Name=Dialog (pimpl) with a UI File Name[ca]=Diàleg (pimpl) amb un fitxer IU Name[ca@valencia]=Diàleg (pimpl) amb un fitxer IU Name[de]=Dialog (pimpl) mit einer UI-Datei -Name[el]=Διάλογος (pimpl) με UI αρχείο Name[en_GB]=Dialogue (pimpl) with a UI File Name[es]=Diálogo (pimpl) con un archivo de interfaz gráfica Name[et]=Dialoog (pimpl) UI-failiga Name[fr]=Dialogue (pimpl) avec un fichier d'interface utilisateur Name[gl]=Dialogo (punteiro opaco) cun ficheiro de interface de usuario Name[it]=Finestra di dialogo (pimpl) con un file UI Name[nl]=Dialoog (pimpl) met een UI-bestand Name[pl]=Okno dialogowe (pimpl) z plikiem UI Name[pt]=Janela (pimpl) com um Ficheiro UI Name[pt_BR]=Caixa de diálogo (pimpl) com um arquivo UI Name[sk]=Dialóg (pimpl) s UI File Name[sl]=Pogovorno okno (pimpl) z datoteko uporabniškega vmesnika Name[sv]=Dialogruta (pimpl) med en UI-fil Name[tr]=UI dosyalı İletişim Penceresi (pimpl) Name[uk]=Діалогове вікно (pimpl) із файлом інтерфейсу користувача (UI) Name[x-test]=xxDialog (pimpl) with a UI Filexx Name[zh_CN]=使用 UI 文件的对话框 (pimpl) Comment=QDialog subclass with a separate Qt Designer file and private implementation Comment[ca]=Subclasse QDialog amb un fitxer separat del Qt Designer i una implementació privada Comment[ca@valencia]=Subclasse QDialog amb un fitxer separat del Qt Designer i una implementació privada Comment[de]=QDialog-Subklasse mit einer separaten Qt-Designer-Datei und privater Implementierung -Comment[el]=QDialog subclass με χωριστό Qt Designer αρχείο και private implementation Comment[en_GB]=QDialog subclass with a separate Qt Designer file and private implementation Comment[es]=Subclase de QDialog con un archivo separado del Diseñador de Qt y una implementación privada Comment[et]=QDialogi alamklass eraldi Qt Designeri failiga ja privaatse teostusega Comment[fr]=Sous-classe QDialog avec un fichier Qt  Designer séparé et une implémentation privée Comment[gl]=Subclase de QDialog cun ficheiro de Qt Designer por separado e unha codificación privada. Comment[it]=Sottoclasse di QDialog con un file separato di Qt Designer e implementazione privata Comment[nl]=QDialog subklasse met een apart Qt Designer bestand en private implementatie Comment[pl]=Podklasa QDialog z osobnym plikiem Projektanta Qt i prywatną implementacją Comment[pt]=Sub-classe de QDialog com um ficheiro separado do Qt Designer e uma implementação privada Comment[pt_BR]=Subclasse de QDialog com um arquivo separado do Qt Designer e uma implementação privada Comment[sk]=Podtrieda QWidget so samostatným súborom Qt Designer a súkromnou implementáciou Comment[sl]=Pod-razred QDialog z ločeno datoteko Qt Designer-ja in zasebno izvedbo Comment[sv]=QDialog-delklass med en separat Qt Designer-fil och privat implementering Comment[tr]=Ayrı bir Qt Designer dosyasına ve özel uygulamasına sahip QDialog alt sınıfı -Comment[uk]=Підклас QDialog із окремим файлом Qt Designer і закритою (private) реалізацією +Comment[uk]=Підклас QDialog із окремим файлом Qt і закритою (private) реалізацією Comment[x-test]=xxQDialog subclass with a separate Qt Designer file and private implementationxx Comment[zh_CN]=包含一个独立的 Qt Designer 文件和私有实现的 QDialog 子类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QDialog Files=Header,PrivateHeader,Implementation,UI OptionsFile=options.kcfg [Header] Name=Public Header Name[bs]=Javno zaglavlje Name[ca]=Capçalera pública Name[ca@valencia]=Capçalera pública Name[da]=Offentlig header Name[de]=Public Header Name[el]=Public Header Name[en_GB]=Public Header Name[es]=Cabecera pública Name[et]=Avalik päis Name[fi]=Julkinen otsikkotiedosto Name[fr]=En-tête public Name[gl]=Cabeceira pública Name[hu]=Publikus fejléc Name[it]=Intestazione pubblica Name[kk]=Ашық (public) айдар Name[nb]=Public hode Name[nl]=Publieke kop Name[pl]=Publiczny Nagłówek Name[pt]=Inclusão Pública Name[pt_BR]=Inclusão pública Name[ru]=Открытый заголовок Name[sk]=Verejná hlavička Name[sl]=Javna glava Name[sv]=Public-deklaration Name[tr]=Genel Başlık Name[uk]=Відкритий (public) заголовок Name[x-test]=xxPublic Headerxx Name[zh_CN]=公有头文件 Name[zh_TW]=公開標頭 File=class.h OutputFile={{ name }}.h [PrivateHeader] Name=Private Header Name[bs]=Privatno zaglavlje Name[ca]=Capçalera privada Name[ca@valencia]=Capçalera privada Name[da]=Privat header Name[de]=Privater Header Name[el]=Private Header Name[en_GB]=Private Header Name[es]=Cabecera privada Name[et]=Privaatpäis Name[fi]=Yksityinen otsikkotiedosto Name[fr]=En-tête privé Name[gl]=Cabeceira privada Name[hu]=Privát fejléc Name[it]=Intestazione privata Name[kk]=Сырлы (private) айдар Name[nb]=Private hode Name[nl]=Private kop Name[pl]=Prywatny Nagłówek Name[pt]=Inclusão Privada Name[pt_BR]=Inclusão privada Name[ru]=Закрытый заголовок Name[sk]=Súkromná hlavička Name[sl]=Zasebna glava Name[sv]=Privat deklaration Name[tr]=Özel Başlık Name[uk]=Закритий (private) заголовок Name[x-test]=xxPrivate Headerxx Name[zh_CN]=私有头文件 Name[zh_TW]=私密標頭 File=class_p.h OutputFile={{ name }}_p.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp [UI] Name=User Interface Name[bg]=Потребителски интерфейс Name[bs]=Korisnički interfejs Name[ca]=Interfície d'usuari Name[ca@valencia]=Interfície d'usuari Name[cs]=Uživatelské rozhraní Name[da]=Brugerflade Name[de]=Benutzeroberfläche Name[el]=Περιβάλλον χρήστη Name[en_GB]=User Interface Name[es]=Interfaz de usuario Name[et]=Kasutajaliides Name[fi]=Käyttöliittymä Name[fr]=Interface utilisateur Name[gl]=Interface de usuario Name[hu]=Felhasználói felület Name[it]=Interfaccia utente Name[kk]=Пайдаланушы интерфейсі Name[lv]=Lietotāja saskarne Name[mr]=वापरकर्ता संवाद Name[nb]=Brukerflate Name[nds]=Böversiet Name[nl]=Gebruikersinterface Name[pa]=ਯੂਜ਼ਰ ਇੰਟਰਫੇਸ Name[pl]=Interfejs użytkownika Name[pt]=Interface do Utilizador Name[pt_BR]=Interface do usuário Name[ro]=Interfață utilizator Name[ru]=Пользовательский интерфейс Name[sk]=Užívateľské rozhranie Name[sl]=Uporabniški vmesnik Name[sv]=Användargränssnitt Name[tr]=Kullanıcı arayüzü Name[ug]=كۆرۈنمەيۈز Name[uk]=Інтерфейс користувача Name[x-test]=xxUser Interfacexx Name[zh_CN]=用户界面 Name[zh_TW]=使用者介面 File=class.ui OutputFile={{ name }}.ui diff --git a/file_templates/classes/qobject_pimpl/qobject_pimpl.desktop b/file_templates/classes/qobject_pimpl/qobject_pimpl.desktop index 08d064b2dd..60cf974aec 100644 --- a/file_templates/classes/qobject_pimpl/qobject_pimpl.desktop +++ b/file_templates/classes/qobject_pimpl/qobject_pimpl.desktop @@ -1,195 +1,193 @@ [General] Name=QObject pimpl subclass Name[ca]=Subclasse QObject «pimpl» Name[ca@valencia]=Subclasse QObject «pimpl» Name[cs]=Podtřída QObject pimpl Name[de]=QObject-Unterklasse (pimpl) -Name[el]=QObject pimpl subclass Name[en_GB]=QObject pimpl subclass Name[es]=Subclase «pimpl» de QObject Name[et]=QObject pimpl alamklass Name[fr]=Sous-classe QObject Name[gl]=Subclase de QObject con punteiro opaco Name[it]=Sottoclasse pimpl di QObject Name[nl]=pimpl subklasse van QObject Name[pl]=Podklasa QObject pimpl Name[pt]=Sub-classe 'pimpl' de QObject Name[pt_BR]=Subclasse pimpl de QObject Name[sk]=Podtrieda QObject pimpl Name[sl]=Pod-razred pimpl za QObject Name[sv]=QObject pimpl-delklass Name[tr]=QObject pimpl alt sınıfı Name[uk]=Підклас pimpl QObject Name[x-test]=xxQObject pimpl subclassxx Name[zh_CN]=QObject pimpl 子类 Comment=QObject subclass with private implementation Comment[ca]=Una subclasse QObject amb implementació privada Comment[ca@valencia]=Una subclasse QObject amb implementació privada Comment[de]=QObject-Unterklasse mit privater Implementierung -Comment[el]=Μια GObject subclass με private implementation Comment[en_GB]=QObject subclass with private implementation Comment[es]=Una subclase de QObject con una implementación privada Comment[et]=QObjecti alamklass privaatse teostusega Comment[fr]=Une sous-classe QObject avec une implémentation privée Comment[gl]=Subclase de QObject cunha codificación privada. Comment[it]=Una sottoclasse di QObject con un'implementazione privata Comment[nl]=Een subklasse van QObject met een private implementatie Comment[pl]=Podklasa GObject z prywatną implementacją Comment[pt]=Uma sub-classe de QObject com uma implementação privada Comment[pt_BR]=Subclasse de QObject com uma implementação privada Comment[sk]=Podtrieda GObject so súkromnou implementáciou Comment[sl]=Pod-razred QObject z zasebno izvedbo Comment[sv]=QObject-delklass med privat implementering Comment[tr]=Gizli gerçeklemeli QObject alt sınıfı Comment[uk]=Підклас QObject із закритою (private) реалізацією Comment[x-test]=xxQObject subclass with private implementationxx Comment[zh_CN]=有私有实现的 QObject 子类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QObject Files=Header,PrivateHeader,Implementation [Header] Name=Header Name[bs]=Zaglavlje Name[ca]=Capçalera Name[ca@valencia]=Capçalera Name[cs]=Hlavička Name[da]=Header Name[de]=Header Name[el]=Header Name[en_GB]=Header Name[es]=Cabecera Name[et]=Päis Name[fi]=Otsikkotiedosto Name[fr]=En-tête Name[gl]=Cabeceira Name[hu]=Fejléc Name[it]=Intestazione Name[kk]=Айдар Name[mr]=हेडर Name[nb]=Hode Name[nl]=Kop Name[pl]=Nagłówek Name[pt]=Inclusão Name[pt_BR]=Cabeçalho Name[ru]=Заголовок Name[sk]=Hlavička Name[sl]=Glava Name[sv]=Deklaration Name[tr]=Başlık Name[ug]=بەت قېشى Name[uk]=Заголовок Name[x-test]=xxHeaderxx Name[zh_CN]=报头 Name[zh_TW]=標頭 File=class.h OutputFile={{ name }}.h [PrivateHeader] Name=Private Header Name[bs]=Privatno zaglavlje Name[ca]=Capçalera privada Name[ca@valencia]=Capçalera privada Name[da]=Privat header Name[de]=Privater Header Name[el]=Private Header Name[en_GB]=Private Header Name[es]=Cabecera privada Name[et]=Privaatpäis Name[fi]=Yksityinen otsikkotiedosto Name[fr]=En-tête privé Name[gl]=Cabeceira privada Name[hu]=Privát fejléc Name[it]=Intestazione privata Name[kk]=Сырлы (private) айдар Name[nb]=Private hode Name[nl]=Private kop Name[pl]=Prywatny Nagłówek Name[pt]=Inclusão Privada Name[pt_BR]=Inclusão privada Name[ru]=Закрытый заголовок Name[sk]=Súkromná hlavička Name[sl]=Zasebna glava Name[sv]=Privat deklaration Name[tr]=Özel Başlık Name[uk]=Закритий (private) заголовок Name[x-test]=xxPrivate Headerxx Name[zh_CN]=私有头文件 Name[zh_TW]=私密標頭 File=class_p.h OutputFile={{ name }}_p.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp diff --git a/file_templates/classes/qt_interface/qt_interface.desktop b/file_templates/classes/qt_interface/qt_interface.desktop index bb645ca741..dc1a5829f2 100644 --- a/file_templates/classes/qt_interface/qt_interface.desktop +++ b/file_templates/classes/qt_interface/qt_interface.desktop @@ -1,158 +1,156 @@ [General] Name=QObject interface class Name[ca]=Classe d'interfície QObject Name[ca@valencia]=Classe d'interfície QObject Name[cs]=Třída QObject interface Name[de]=QObject-Schnittstellenklasse -Name[el]=QObject interface subclass Name[en_GB]=QObject interface class Name[es]=Clase de interfaz para QObject Name[et]=QObjecti liidese klass Name[fr]=Classe d'interface QObject Name[gl]=Clase de interface de QObject Name[it]=Classe interfaccia di QObject Name[nl]=Interfaceklasse van QObject Name[pl]=Podklasa QObject interfejsu Name[pt]=Classe-interface de QObject Name[pt_BR]=Classe-interface de QObject Name[sk]=Trieda rozhrania QObject Name[sl]=Pod-razred vmesnika QObject Name[sv]=QObject-gränssnittsklass Name[tr]=QObject arayüz sınıfı Name[uk]=Клас інтерфейсу QObject Name[x-test]=xxQObject interface classxx Name[zh_CN]=QObject 接口类 Comment=QObject interface class with properties Comment[ca]=Classe d'interfície QObject amb propietats Comment[ca@valencia]=Classe d'interfície QObject amb propietats Comment[de]=QObject-Schnittstellenklasse mit Eigenschaften -Comment[el]=QObject interface subclass με ιδιότητες Comment[en_GB]=QObject interface class with properties Comment[es]=Clase de interfaz para QObject con propiedades Comment[et]=QObjecti liidese klass omadustega Comment[fr]=Interface QObject avec des propriétés Comment[gl]=Clase de interface de QObject con propiedades. Comment[it]=Classe interfaccia di QObject con delle proprietà Comment[nl]=Interfaceklasse van QObject met eigenschappen Comment[pl]=Podklasa QObject interfejsu z właściwościami Comment[pt]=Classe-interface de QObject com propriedades Comment[pt_BR]=Classe-interface de QObject com propriedades Comment[sk]=Trieda rozhrania QObject s vlastnosťami Comment[sl]=Pod-razred vmesnika QObject z lastnostmi Comment[sv]=QObject-gränssnittsklass med egenskaper Comment[tr]=Özellikleriyle QObject arayüzü alt sınıfı Comment[uk]=Клас інтерфейсу QObject з властивостями Comment[x-test]=xxQObject interface class with propertiesxx Comment[zh_CN]=有属性的 QObject 接口类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class Files=Header,Implementation OptionsFile=options.kcfg [Header] Name=Public Header Name[bs]=Javno zaglavlje Name[ca]=Capçalera pública Name[ca@valencia]=Capçalera pública Name[da]=Offentlig header Name[de]=Public Header Name[el]=Public Header Name[en_GB]=Public Header Name[es]=Cabecera pública Name[et]=Avalik päis Name[fi]=Julkinen otsikkotiedosto Name[fr]=En-tête public Name[gl]=Cabeceira pública Name[hu]=Publikus fejléc Name[it]=Intestazione pubblica Name[kk]=Ашық (public) айдар Name[nb]=Public hode Name[nl]=Publieke kop Name[pl]=Publiczny Nagłówek Name[pt]=Inclusão Pública Name[pt_BR]=Inclusão pública Name[ru]=Открытый заголовок Name[sk]=Verejná hlavička Name[sl]=Javna glava Name[sv]=Public-deklaration Name[tr]=Genel Başlık Name[uk]=Відкритий (public) заголовок Name[x-test]=xxPublic Headerxx Name[zh_CN]=公有头文件 Name[zh_TW]=公開標頭 File=interface.h OutputFile={{ name }}.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=interface.cpp OutputFile={{ name }}.cpp diff --git a/file_templates/classes/qt_widget/qt_widget.desktop b/file_templates/classes/qt_widget/qt_widget.desktop index 830b414051..079d627c36 100644 --- a/file_templates/classes/qt_widget/qt_widget.desktop +++ b/file_templates/classes/qt_widget/qt_widget.desktop @@ -1,215 +1,215 @@ [General] Name=Widget with a UI File Name[bs]=Grafička kontrola s UI datotekom Name[ca]=Estri amb un fitxer IU -Name[ca@valencia]=Giny («Widget») amb un fitxer IU +Name[ca@valencia]=Estri amb un fitxer IU Name[da]=Widget med en UI-fil Name[de]=Bedienelement mit einer UI-Datei Name[el]=Γραφικό συστατικό με UI αρχείο Name[en_GB]=Widget with a UI File Name[es]=Widget con un archivo de interfaz gráfica Name[et]=Vidin UI-failiga Name[fi]=Käyttöliittymäelementti UI-tiedostolla Name[fr]=Composant graphique avec un fichier d'interface utilisateur Name[gl]=Trebello cun ficheiro para UI Name[hu]=Felületi elem UI fájllal Name[it]=Oggetto con un file UI Name[kk]=Пайдаланушы интерфейс файлдағы виджет Name[nb]=Skjermelement med en UI-fil Name[nl]=Widget met een UI-bestand Name[pl]=Element interfejsu z plikiem interfejsu użytkownika Name[pt]=Item Gráfico com um Ficheiro UI Name[pt_BR]=Widget com um arquivo UI Name[ru]=Виджет с файлом .ui Name[sk]=Widget s UI súborom Name[sl]=Gradnik z datoteko uporabniškega vmesnika Name[sv]=Grafisk komponent med en UI-fil Name[tr]=UI dosyalı programcık Name[uk]=Віджет з файлом інтерфейсу користувача Name[x-test]=xxWidget with a UI Filexx Name[zh_CN]=带有一个 UI 文件的部件 Name[zh_TW]=有 UI 檔的元件 Comment=QWidget subclass with a separate Qt Designer file Comment[bs]=QWidget podklasa s odvojenom Qt Designer datotekom Comment[ca]=Subclasse QWidget amb un fitxer separat del Qt Designer Comment[ca@valencia]=Subclasse QWidget amb un fitxer separat del Qt Designer Comment[da]=Underklasse af QWidget med en separat Qt Designer-fil Comment[de]=QWidget-Subklasse mit einer separaten Qt-Designer-Datei Comment[el]=QWidget subclass με χωριστό Qt Designer αρχείο Comment[en_GB]=QWidget subclass with a separate Qt Designer file Comment[es]=Subclase de QWidget con un archivo separado del Diseñador de Qt Comment[et]=QWidgeti alamklass eraldi Qt Designeri failiga Comment[fi]=QWidget-aliluokka, jolla on erillinen Qt Designer -tiedosto Comment[fr]=Sous-classe QWidget avec un fichier Qt  Designer séparé Comment[gl]=Subclase de QWidget cun ficheiro separado para Qt Designer Comment[hu]=QWidget alosztály különálló Qt tervező fájllal Comment[it]=Sottoclasse QWidget con un file separato di Qt Designer Comment[kk]=Бөлек Qt Designer файлдағы QWidget Comment[nb]=QWidget subklasse med en separat Qt Designer-fil Comment[nl]=Subklasse QWidget met een aparte Qt-designer bestand Comment[pl]=Podklasa QWidget z osobnym plikiem Projektanta Qt Comment[pt]=Sub-classe de QWidget com um ficheiro separado do Qt Designer Comment[pt_BR]=Subclasse de QWidget com um arquivo Qt Designer separado Comment[ru]=Подкласс QWidget с описанием интерфейса в формате Qt Designer Comment[sk]=Podtrieda QWidget so samostatným súborom Qt Designer Comment[sl]=Pod-razred QWidget z ločeno datoteko Qt Designer-ja Comment[sv]=QWidget-delklass med en separat Qt Designer-fil Comment[tr]=Ayrı bir Qt Designer dosyası olan QWidget alt sınıfı Comment[uk]=Підклас QWidget з окремим файлом Qt Designer Comment[x-test]=xxQWidget subclass with a separate Qt Designer filexx Comment[zh_CN]=带有一个单独的 Qt 设计师文件的 QWidget 子类 Comment[zh_TW]=QWidget 子類別,含個別的 Qt 設計師檔 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QWidget Files=Header,Implementation,UI [Header] Name=Public Header Name[bs]=Javno zaglavlje Name[ca]=Capçalera pública Name[ca@valencia]=Capçalera pública Name[da]=Offentlig header Name[de]=Public Header Name[el]=Public Header Name[en_GB]=Public Header Name[es]=Cabecera pública Name[et]=Avalik päis Name[fi]=Julkinen otsikkotiedosto Name[fr]=En-tête public Name[gl]=Cabeceira pública Name[hu]=Publikus fejléc Name[it]=Intestazione pubblica Name[kk]=Ашық (public) айдар Name[nb]=Public hode Name[nl]=Publieke kop Name[pl]=Publiczny Nagłówek Name[pt]=Inclusão Pública Name[pt_BR]=Inclusão pública Name[ru]=Открытый заголовок Name[sk]=Verejná hlavička Name[sl]=Javna glava Name[sv]=Public-deklaration Name[tr]=Genel Başlık Name[uk]=Відкритий (public) заголовок Name[x-test]=xxPublic Headerxx Name[zh_CN]=公有头文件 Name[zh_TW]=公開標頭 File=class.h OutputFile={{ name }}.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp [UI] Name=User Interface Name[bg]=Потребителски интерфейс Name[bs]=Korisnički interfejs Name[ca]=Interfície d'usuari Name[ca@valencia]=Interfície d'usuari Name[cs]=Uživatelské rozhraní Name[da]=Brugerflade Name[de]=Benutzeroberfläche Name[el]=Περιβάλλον χρήστη Name[en_GB]=User Interface Name[es]=Interfaz de usuario Name[et]=Kasutajaliides Name[fi]=Käyttöliittymä Name[fr]=Interface utilisateur Name[gl]=Interface de usuario Name[hu]=Felhasználói felület Name[it]=Interfaccia utente Name[kk]=Пайдаланушы интерфейсі Name[lv]=Lietotāja saskarne Name[mr]=वापरकर्ता संवाद Name[nb]=Brukerflate Name[nds]=Böversiet Name[nl]=Gebruikersinterface Name[pa]=ਯੂਜ਼ਰ ਇੰਟਰਫੇਸ Name[pl]=Interfejs użytkownika Name[pt]=Interface do Utilizador Name[pt_BR]=Interface do usuário Name[ro]=Interfață utilizator Name[ru]=Пользовательский интерфейс Name[sk]=Užívateľské rozhranie Name[sl]=Uporabniški vmesnik Name[sv]=Användargränssnitt Name[tr]=Kullanıcı arayüzü Name[ug]=كۆرۈنمەيۈز Name[uk]=Інтерфейс користувача Name[x-test]=xxUser Interfacexx Name[zh_CN]=用户界面 Name[zh_TW]=使用者介面 File=class.ui OutputFile={{ name }}.ui diff --git a/file_templates/classes/qwidget_pimpl/qwidget_pimpl.desktop b/file_templates/classes/qwidget_pimpl/qwidget_pimpl.desktop index 0358b10ffd..b64325348c 100644 --- a/file_templates/classes/qwidget_pimpl/qwidget_pimpl.desktop +++ b/file_templates/classes/qwidget_pimpl/qwidget_pimpl.desktop @@ -1,231 +1,227 @@ [General] Name=Widget (pimpl) with a UI File Name[ca]=Estri (pimpl) amb un fitxer IU -Name[ca@valencia]=Giny («Widget») (pimpl) amb un fitxer IU +Name[ca@valencia]=Estri (pimpl) amb un fitxer IU Name[de]=Bedienelement (pimpl) mit einer UI-Datei -Name[el]=Γραφικό συστατικό (pimpl) με UI αρχείο Name[en_GB]=Widget (pimpl) with a UI File Name[es]=Widget (pimpl) con un archivo de interfaz gráfica Name[et]=Vidin (pimpl) UI-failiga Name[fr]=Composant graphique (pimpl) avec un fichier d'interface utilisateur Name[gl]=Trebello (punteiro opaco) cun ficheiro de interface de usuario Name[it]=Oggetto (pimpl) con un file UI Name[nl]=Widget (pimpl) met een UI-bestand -Name[pl]=Widget (pimpl) z plikiem UI +Name[pl]=Element interfejsu (pimpl) z plikiem UI Name[pt]=Janela (pimpl) com um Ficheiro UI Name[pt_BR]=Widget (pimpl) com um arquivo UI -Name[sk]=Widget (pimpl) s UI súborom -Name[sl]=Gradnik (pimpl) z datoteko uporabniškega vmesnika +Name[sk]=Widget (pimpl) so súborom UI Name[sv]=Grafisk komponent (pimpl) med en UI-fil Name[uk]=Віджет (pimpl) із файлом UI Name[x-test]=xxWidget (pimpl) with a UI Filexx Name[zh_CN]=使用 UI 文件的部件 (pimpl) Comment=QWidget subclass with a separate Qt Designer file and private implementation Comment[ca]=Subclasse QWidget amb un fitxer separat del Qt Designer i una implementació privada Comment[ca@valencia]=Subclasse QWidget amb un fitxer separat del Qt Designer i una implementació privada Comment[de]=QWidget-Unterklasse mit einer separaten Qt-Designer-Datei und privater Implementation -Comment[el]=QWidget subclass με χωριστό Qt Designer αρχείο και private implementation Comment[en_GB]=QWidget subclass with a separate Qt Designer file and private implementation Comment[es]=Subclase de QWidget con un archivo separado del Diseñador de Qt y una implementación privada Comment[et]=QWidgeti alamklass eraldi Qt Designeri failiga ja privaatse teostusega Comment[fr]=Sous-classe QWidget avec un fichier Qt Designer séparé et une implémentation privée Comment[gl]=Subclase de Widget cun ficheiro de Qt Designer por separado e unha codificación privada. Comment[it]=Sottoclasse di QWidget con un file separato di Qt Designer e implementazione privata Comment[nl]=QWidget subclass met een apart Qt Designer bestand en privé implementatie Comment[pl]=Podklasa QWidget z osobnym plikiem Projektanta Qt i prywatną implementacją Comment[pt]=Sub-classe de QWidget com um ficheiro separado do Qt Designer e uma implementação privada Comment[pt_BR]=Subclasse de QWidget com um arquivo separado do Qt Designer e uma implementação privada Comment[sk]=Podtrieda QWidget so samostatným súborom Qt Designer a súkromnou implementáciou -Comment[sl]=Pod-razred QWidget z ločeno datoteko Qt Designer-ja in zasebno izvedbo Comment[sv]=QWidget-delklass med en separat Qt Designer-fil och privat implementering Comment[uk]=Підклас QWidget із окремим файлом Qt Designer і закритою (private) реалізацією Comment[x-test]=xxQWidget subclass with a separate Qt Designer file and private implementationxx Comment[zh_CN]=包含一个独立的 Qt Designer 文件和私有实现的 QWidget 子类 Category=C++/Qt Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Class BaseClasses=public QWidget Files=Header,PrivateHeader,Implementation,UI [Header] Name=Public Header Name[bs]=Javno zaglavlje Name[ca]=Capçalera pública Name[ca@valencia]=Capçalera pública Name[da]=Offentlig header Name[de]=Public Header Name[el]=Public Header Name[en_GB]=Public Header Name[es]=Cabecera pública Name[et]=Avalik päis Name[fi]=Julkinen otsikkotiedosto Name[fr]=En-tête public Name[gl]=Cabeceira pública Name[hu]=Publikus fejléc Name[it]=Intestazione pubblica Name[kk]=Ашық (public) айдар Name[nb]=Public hode Name[nl]=Publieke kop Name[pl]=Publiczny Nagłówek Name[pt]=Inclusão Pública Name[pt_BR]=Inclusão pública Name[ru]=Открытый заголовок Name[sk]=Verejná hlavička Name[sl]=Javna glava Name[sv]=Public-deklaration Name[tr]=Genel Başlık Name[uk]=Відкритий (public) заголовок Name[x-test]=xxPublic Headerxx Name[zh_CN]=公有头文件 Name[zh_TW]=公開標頭 File=class.h OutputFile={{ name }}.h [PrivateHeader] Name=Private Header Name[bs]=Privatno zaglavlje Name[ca]=Capçalera privada Name[ca@valencia]=Capçalera privada Name[da]=Privat header Name[de]=Privater Header Name[el]=Private Header Name[en_GB]=Private Header Name[es]=Cabecera privada Name[et]=Privaatpäis Name[fi]=Yksityinen otsikkotiedosto Name[fr]=En-tête privé Name[gl]=Cabeceira privada Name[hu]=Privát fejléc Name[it]=Intestazione privata Name[kk]=Сырлы (private) айдар Name[nb]=Private hode Name[nl]=Private kop Name[pl]=Prywatny Nagłówek Name[pt]=Inclusão Privada Name[pt_BR]=Inclusão privada Name[ru]=Закрытый заголовок Name[sk]=Súkromná hlavička Name[sl]=Zasebna glava Name[sv]=Privat deklaration Name[tr]=Özel Başlık Name[uk]=Закритий (private) заголовок Name[x-test]=xxPrivate Headerxx Name[zh_CN]=私有头文件 Name[zh_TW]=私密標頭 File=class_p.h OutputFile={{ name }}_p.h [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}.cpp [UI] Name=User Interface Name[bg]=Потребителски интерфейс Name[bs]=Korisnički interfejs Name[ca]=Interfície d'usuari Name[ca@valencia]=Interfície d'usuari Name[cs]=Uživatelské rozhraní Name[da]=Brugerflade Name[de]=Benutzeroberfläche Name[el]=Περιβάλλον χρήστη Name[en_GB]=User Interface Name[es]=Interfaz de usuario Name[et]=Kasutajaliides Name[fi]=Käyttöliittymä Name[fr]=Interface utilisateur Name[gl]=Interface de usuario Name[hu]=Felhasználói felület Name[it]=Interfaccia utente Name[kk]=Пайдаланушы интерфейсі Name[lv]=Lietotāja saskarne Name[mr]=वापरकर्ता संवाद Name[nb]=Brukerflate Name[nds]=Böversiet Name[nl]=Gebruikersinterface Name[pa]=ਯੂਜ਼ਰ ਇੰਟਰਫੇਸ Name[pl]=Interfejs użytkownika Name[pt]=Interface do Utilizador Name[pt_BR]=Interface do usuário Name[ro]=Interfață utilizator Name[ru]=Пользовательский интерфейс Name[sk]=Užívateľské rozhranie Name[sl]=Uporabniški vmesnik Name[sv]=Användargränssnitt Name[tr]=Kullanıcı arayüzü Name[ug]=كۆرۈنمەيۈز Name[uk]=Інтерфейс користувача Name[x-test]=xxUser Interfacexx Name[zh_CN]=用户界面 Name[zh_TW]=使用者介面 File=class.ui OutputFile={{ name }}.ui diff --git a/file_templates/testing/cpp_cpputest/cpp_cpputest.desktop b/file_templates/testing/cpp_cpputest/cpp_cpputest.desktop index 2380cbe299..3f31bed975 100644 --- a/file_templates/testing/cpp_cpputest/cpp_cpputest.desktop +++ b/file_templates/testing/cpp_cpputest/cpp_cpputest.desktop @@ -1,126 +1,124 @@ [General] Name=CppUTest Name[ca]=CppUTest Name[ca@valencia]=CppUTest Name[de]=CppUTest -Name[el]=CppUTest Name[en_GB]=CppUTest Name[es]=CppUTest Name[et]=CppUTest Name[fi]=CppUTest Name[fr]=CppUTest Name[gl]=CppUTest Name[it]=CppUTest Name[nl]=CppUTest Name[pl]=CppUTest Name[pt]=CppUTest Name[pt_BR]=CppUTest Name[ru]=CppUTest Name[sk]=CppUTest Name[sl]=CppUTest Name[sv]=CppUTest Name[tr]=CppUTest Name[uk]=CppUTest Name[x-test]=xxCppUTestxx Name[zh_CN]=CppUTest Comment=A unit test using the CppUTest library Comment[ca]=Una prova unitària utilitzant la biblioteca CppUTest Comment[ca@valencia]=Una prova unitària utilitzant la biblioteca CppUTest Comment[de]=Ein Unit-Test, der die CppUTest-Bibliothek verwendet -Comment[el]=Ένα unit test με χρήση του CppUTest library Comment[en_GB]=A unit test using the CppUTest library Comment[es]=Una prueba unitaria que usa la infraestructura CppUTest Comment[et]=Ühiktest CppUTesti teegi abil Comment[fi]=Yksikkötesti käyttäen CppUTest-kirjastoa Comment[fr]=Un test unitaire utilisant la bibliothèque CppUTest Comment[gl]=Unha proba unitaria que usa a biblioteca CppUTest. Comment[it]=Un unit test che usa la libreria CppUTest Comment[nl]=Een test van een eenheid met gebruik van de bibliotheek CppUTest Comment[pl]=Jednostkowy test wykorzystujący bibliotekę CppUTest Comment[pt]=Um teste unitário que usa a plataforma CppUTest Comment[pt_BR]=Teste unitário que usa a biblioteca CppUTest Comment[ru]=Модульный тест на базе библиотеки CppUTest Comment[sk]=Unit test pomocou frameworku CppUTest Comment[sl]=Preizkus enote z uporabo knjižnice CppUTest Comment[sv]=En enhetstest som använder biblioteket CppUTest Comment[tr]=CppUTest kitaplığını kullanan bir birim testi Comment[uk]=Перевірка модулів за допомогою бібліотеки CppUTest Comment[x-test]=xxA unit test using the CppUTest libraryxx Comment[zh_CN]=一个使用 CppUTest 库的单元测试 Category=C++/Basic Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Test Files=Implementation [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}_test.cpp diff --git a/file_templates/testing/cpp_gtest/cpp_gtest.desktop b/file_templates/testing/cpp_gtest/cpp_gtest.desktop index 75ffe297d1..677612857b 100644 --- a/file_templates/testing/cpp_gtest/cpp_gtest.desktop +++ b/file_templates/testing/cpp_gtest/cpp_gtest.desktop @@ -1,126 +1,124 @@ [General] Name=Google Test Name[ca]=Google Test Name[ca@valencia]=Google Test -Name[de]=Google Test -Name[el]=Google Test +Name[de]=Google-Test Name[en_GB]=Google Test Name[es]=Google Test Name[et]=Google Test Name[fi]=Google Test Name[fr]=Google Test Name[gl]=Google Test Name[it]=Google Test Name[nl]=Google Test Name[pl]=Google Test Name[pt]=Teste do Google Name[pt_BR]=Google Test Name[ru]=Google Test Name[sk]=Google Test Name[sl]=Google Test Name[sv]=Google Test Name[tr]=Google Testi Name[uk]=Google Test Name[x-test]=xxGoogle Testxx Name[zh_CN]=Google Test Comment=A unit test using the Google Test library Comment[ca]=Una prova unitària utilitzant la biblioteca Test de Google Comment[ca@valencia]=Una prova unitària utilitzant la biblioteca Test de Google Comment[de]=Ein Unit-Test, der die Google-Test-Bibliothek verwendet -Comment[el]=Ένα unit test με χρήση της βιβλιοθήκης Google Test Comment[en_GB]=A unit test using the Google Test library Comment[es]=Una prueba unitaria que usa la biblioteca Google Test Comment[et]=Ühiktest Google Testi teegi abil Comment[fi]=Yksikkötesti käyttäen Google Test -kirjastoa Comment[fr]=Un test unitaire utilisant la bibliothèque Google Test Comment[gl]=Unha proba unitaria que usa a biblioteca Google Test. Comment[it]=Un unit test che usa la libreria Google Test Comment[nl]=Een test van een eenheid met gebruik van de bibliotheek Google Test Comment[pl]=Jednostkowy test wykorzystujący bibliotekę Google Test -Comment[pt]=Um teste unitário que usa a plataforma de testes da Google +Comment[pt]=Um teste unitário que usa a plataforma de testes do Google Comment[pt_BR]=Teste unitário que usa a biblioteca Google Test Comment[ru]=Модульный тест на базе библиотеки Google Test Comment[sk]=Unit test pomocou knižnice Google Test Comment[sl]=Preizkus enote z uporabo knjižnice Google Test Comment[sv]=En enhetstest som använder biblioteket Google Test Comment[tr]=Google Test kitaplığını kullanan birim testi Comment[uk]=Перевірка модулів за допомогою бібліотеки Google Test Comment[x-test]=xxA unit test using the Google Test libraryxx Comment[zh_CN]=一个使用 Google Test 库的单元测试 Category=C++/Basic Language=C++ Language[bs]=C++ Language[ca]=C++ Language[ca@valencia]=C++ Language[cs]=C++ Language[da]=C++ Language[de]=C++ Language[el]=C++ Language[en_GB]=C++ Language[es]=C++ Language[et]=C++ Language[fi]=C++ Language[fr]=C++ Language[ga]=C++ Language[gl]=C++ Language[hu]=C++ Language[it]=C++ Language[kk]=C++ Language[mr]=C++ Language[nb]=C++ Language[nds]=C++ Language[nl]=C++ Language[nn]=C++ Language[pl]=C++ Language[pt]=C++ Language[pt_BR]=C++ Language[ru]=C++ Language[se]=C++ Language[sk]=C++ Language[sl]=C++ Language[sv]=C++ Language[tr]=C++ Language[ug]=C++ Language[uk]=C++ Language[x-test]=xxC++xx Language[zh_CN]=C++ Language[zh_TW]=C++ Type=Test Files=Implementation [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=class.cpp OutputFile={{ name }}_test.cpp diff --git a/kdevplatform/interfaces/kdevelopplugin.desktop b/kdevplatform/interfaces/kdevelopplugin.desktop index 37180a9f5d..a33e53cf28 100644 --- a/kdevplatform/interfaces/kdevelopplugin.desktop +++ b/kdevplatform/interfaces/kdevelopplugin.desktop @@ -1,90 +1,89 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=KDevelop/Plugin X-KDE-Derived=KPluginInfo Name=KDevelop Plugin Name[bg]=KDevelop приставка Name[ca]=Connector del KDevelop Name[ca@valencia]=Connector del KDevelop Name[cs]=Modul KDevelop Name[de]=KDevelop-Modul -Name[el]=Πρόσθετο του KDevelop Name[en_GB]=KDevelop Plugin Name[es]=Complemento de KDevelop Name[et]=KDevelopi plugin Name[fr]=Module externe pour KDevelop Name[gl]=Complemento de KDevelop Name[hr]=KDevelop priključak Name[it]=Estensione per KDevelop Name[nb]=KDevelop programtillegg Name[nl]=Plugin van KDevelop Name[pl]=Wtyczka do KDevelopa Name[pt]='Plugin' do KDevelop Name[pt_BR]=Plugin do KDevelop Name[sk]=Plugin KDevelop Name[sl]=Vstavek za KDevelop Name[sv]=KDevelop-insticksprogram Name[tr]=KDevelop Eklentisi Name[uk]=Додаток KDevelop Name[x-test]=xxKDevelop Pluginxx Name[zh_CN]=KDevelop 插件 # mandatory, versioning - prevent DLL hell [PropertyDef::X-KDevelop-Version] Type=int # optional, determines whether a plugin is loaded only after # a project is opened, or is a global plugin. # If it is not set, the plugin can only be loaded by the # user or via requesting one of its dependencies # allowed values: Global, Project [PropertyDef::X-KDevelop-Category] Type=QString # mandatory, GUI-Operation Mode, determines whether a plugin # can work without having a mainwindow/partcontroller # running # allowed values: GUI, NoGUI [PropertyDef::X-KDevelop-Mode] Type=QString # optional, Interfaces that a plugin implements # usually values start with org.kdevelop [PropertyDef::X-KDevelop-Interfaces] Type=QStringList # optional, interfaces that this plugin depends # on [PropertyDef::X-KDevelop-IRequired] Type=QStringList # optional, interfaces that this plugin can use, # but the plugin still works if the interfaces are # not available. [PropertyDef::X-KDevelop-IOptional] Type=QStringList # optional, mimetypes supported by a language plugin [PropertyDef::X-KDevelop-SupportedMimeTypes] Type=QStringList # optional, languages supported by a language plugin # Example language names: "C", "C++", "Objective-C", "CMake" [PropertyDef::X-KDevelop-Languages] Type=QStringList # optional, defines whether the plugin can be disabled # by the user. Possible values are "AlwaysOn" and "UserSelectable". # If the property is missing then UserSelectable is assumed [PropertyDef::X-KDevelop-LoadMode] Type=QString # optional, list of filters for "projectfiles" for the project plugin # For example: Makefile,Makefile.* for Makefile's [PropertyDef::X-KDevelop-ProjectFilesFilter] Type=QStringList # optional, description for the projectfiles filter [PropertyDef::X-KDevelop-ProjectFilesFilterDescription] Type=QString diff --git a/kdevplatform/language/duchain/navigation/problemnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/problemnavigationcontext.cpp index 1a2e19fe07..de5b99725b 100644 --- a/kdevplatform/language/duchain/navigation/problemnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/problemnavigationcontext.cpp @@ -1,273 +1,273 @@ /* Copyright 2009 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemnavigationcontext.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { QString KEY_INVOKE_ACTION(int num) { return QStringLiteral("invoke_action_%1").arg(num); } QString iconForSeverity(IProblem::Severity severity) { switch (severity) { case IProblem::Hint: return QStringLiteral("dialog-information"); case IProblem::Warning: return QStringLiteral("dialog-warning"); case IProblem::Error: return QStringLiteral("dialog-error"); case IProblem::NoSeverity: return {}; } Q_UNREACHABLE(); return {}; } QString htmlImg(const QString& iconName, KIconLoader::Group group) { auto* loader = KIconLoader::global(); const int size = loader->currentSize(group); return QStringLiteral("") .arg(size) .arg(loader->iconPath(iconName, group)); } } ProblemNavigationContext::ProblemNavigationContext(const QVector& problems, const Flags flags) : m_problems(problems) , m_flags(flags) , m_widget(nullptr) { // Sort problems vector: // 1) By severity // 2) By sourceString, if severities are equals std::sort(m_problems.begin(), m_problems.end(), [](const IProblem::Ptr& a, const IProblem::Ptr& b) { if (a->severity() < b->severity()) return true; if (a->severity() > b->severity()) return false; if (a->sourceString() < b->sourceString()) return true; return false; }); } ProblemNavigationContext::~ProblemNavigationContext() { delete m_widget; } QWidget* ProblemNavigationContext::widget() const { return m_widget; } bool ProblemNavigationContext::isWidgetMaximized() const { return false; } QString ProblemNavigationContext::name() const { return i18n("Problem"); } QString ProblemNavigationContext::escapedHtml(const QString& text) const { const QString htmlStart = QStringLiteral(""); const QString htmlEnd = QStringLiteral(""); QString result = text.trimmed(); if (!result.startsWith(htmlStart)) return result.toHtmlEscaped(); result.remove(htmlStart); result.remove(htmlEnd); return result; } void ProblemNavigationContext::html(IProblem::Ptr problem) { auto iconPath = iconForSeverity(problem->severity()); modifyHtml() += QStringLiteral(""); modifyHtml() += QStringLiteral("").arg(htmlImg(iconPath, KIconLoader::Panel)); // BEGIN: right column modifyHtml() += QStringLiteral(""); // END: right column modifyHtml() += QStringLiteral("
%1"); modifyHtml() += i18n("Problem in %1", problem->sourceString()); modifyHtml() += QStringLiteral("
"); if (m_flags & ShowLocation) { modifyHtml() += labelHighlight(i18n("Location: ")); makeLink(QStringLiteral("%1 :%2") .arg(problem->finalLocation().document.toUrl().fileName()) .arg(problem->finalLocation().start().line() + 1), QString(), NavigationAction(problem->finalLocation().document.toUrl(), problem->finalLocation().start()) ); modifyHtml() += QStringLiteral("
"); } QString description = escapedHtml(problem->description()); QString explanation = escapedHtml(problem->explanation()); modifyHtml() += description; // Add only non-empty explanation which differs from the problem description. // Skip this if we have more than one problem. if (m_problems.size() == 1 && !explanation.isEmpty() && explanation != description) modifyHtml() += QLatin1String("

") + explanation + QLatin1String("

"); modifyHtml() += QStringLiteral("
"); - auto diagnostics = problem->diagnostics(); + const auto diagnostics = problem->diagnostics(); if (!diagnostics.isEmpty()) { DUChainReadLocker lock; for (auto diagnostic : diagnostics) { modifyHtml() += QStringLiteral("

"); modifyHtml() += labelHighlight(QStringLiteral("%1: ").arg(diagnostic->severityString())); modifyHtml() += escapedHtml(diagnostic->description()); const DocumentRange range = diagnostic->finalLocation(); Declaration* declaration = DUChainUtils::itemUnderCursor(range.document.toUrl(), range.start()).declaration; if (declaration) { modifyHtml() += i18n("
See: "); makeLink(declaration->toString(), DeclarationPointer(declaration), NavigationAction::NavigateDeclaration); modifyHtml() += i18n(" in "); const auto url = declaration->url().toUrl(); makeLink(QStringLiteral("%1 :%2") .arg(url.fileName()) .arg(declaration->rangeInCurrentRevision().start().line() + 1), url.toDisplayString(QUrl::PreferLocalFile), NavigationAction(url, declaration->rangeInCurrentRevision().start())); } else if (range.start().isValid()) { modifyHtml() += i18n("
See: "); const auto url = range.document.toUrl(); makeLink(QStringLiteral("%1 :%2") .arg(url.fileName()) .arg(range.start().line() + 1), url.toDisplayString(QUrl::PreferLocalFile), NavigationAction(url, range.start())); } modifyHtml() += QStringLiteral("

"); } } auto assistant = problem->solutionAssistant(); if (assistant && !assistant->actions().isEmpty()) { modifyHtml() += QStringLiteral("").arg(QStringLiteral( "#b3d4ff")); modifyHtml() += QStringLiteral(""); modifyHtml() += QStringLiteral("
%1").arg(htmlImg(QStringLiteral( "dialog-ok-apply"), KIconLoader::Panel)); const int startIndex = m_assistantsActions.size(); int currentIndex = startIndex; const auto assistantActions = assistant->actions(); for (auto& assistantAction : assistantActions) { m_assistantsActions.append(assistantAction); if (currentIndex != startIndex) modifyHtml() += QStringLiteral("
"); makeLink(i18n("Solution (%1)", currentIndex + 1), KEY_INVOKE_ACTION(currentIndex), NavigationAction(KEY_INVOKE_ACTION(currentIndex))); modifyHtml() += QLatin1String(": ") + assistantAction->description().toHtmlEscaped(); ++currentIndex; } modifyHtml() += QStringLiteral("
"); } } QString ProblemNavigationContext::html(bool shorten) { AbstractNavigationContext::html(shorten); clear(); m_assistantsActions.clear(); int problemIndex = 0; for (auto& problem : qAsConst(m_problems)) { html(problem); if (++problemIndex != m_problems.size()) modifyHtml() += QStringLiteral("
"); } return currentHtml(); } NavigationContextPointer ProblemNavigationContext::executeKeyAction(const QString& key) { if (key.startsWith(QLatin1String("invoke_action_"))) { const int index = key.midRef(strlen("invoke_action_")).toInt(); executeAction(index); } return {}; } void ProblemNavigationContext::executeAction(int index) { if (index < 0 || index >= m_assistantsActions.size()) return; auto action = m_assistantsActions.at(index); Q_ASSERT(action); if (action) { action->execute(); if (topContext()) DUChain::self()->updateContextForUrl(topContext()->url(), TopDUContext::ForceUpdate); } else { qCWarning(LANGUAGE()) << "No such action"; return; } } diff --git a/kdevplatform/project/projectchangesmodel.cpp b/kdevplatform/project/projectchangesmodel.cpp index ffe3e9fcbe..40d819f397 100644 --- a/kdevplatform/project/projectchangesmodel.cpp +++ b/kdevplatform/project/projectchangesmodel.cpp @@ -1,295 +1,300 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectchangesmodel.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(KDevelop::IProject*) using namespace KDevelop; ProjectChangesModel::ProjectChangesModel(QObject* parent) : VcsFileChangesModel(parent) { const auto projects = ICore::self()->projectController()->projects(); for (IProject* p : projects) { addProject(p); } connect(ICore::self()->projectController(), &IProjectController::projectOpened, this, &ProjectChangesModel::addProject); connect(ICore::self()->projectController(), &IProjectController::projectClosing, this, &ProjectChangesModel::removeProject); connect(ICore::self()->documentController(), &IDocumentController::documentSaved, this, &ProjectChangesModel::documentSaved); connect(ICore::self()->projectController()->projectModel(), &ProjectModel::rowsInserted, this, &ProjectChangesModel::itemsAdded); connect(ICore::self()->runController(), &IRunController::jobUnregistered, this, &ProjectChangesModel::jobUnregistered); } ProjectChangesModel::~ProjectChangesModel() {} void ProjectChangesModel::addProject(IProject* p) { QStandardItem* it = new QStandardItem(p->name()); it->setData(p->name(), ProjectChangesModel::ProjectNameRole); IPlugin* plugin = p->versionControlPlugin(); if(plugin) { auto* vcs = plugin->extension(); auto info = ICore::self()->pluginController()->pluginInfo(plugin); it->setIcon(QIcon::fromTheme(info.iconName())); it->setToolTip(vcs->name()); auto* branchingExtension = plugin->extension(); if(branchingExtension) { const auto pathUrl = p->path().toUrl(); branchingExtension->registerRepositoryForCurrentBranchChanges(pathUrl); // can't use new signal slot syntax here, IBranchingVersionControl is not a QObject connect(plugin, SIGNAL(repositoryBranchChanged(QUrl)), this, SLOT(repositoryBranchChanged(QUrl))); repositoryBranchChanged(pathUrl); } else reload(QList() << p); } else { it->setEnabled(false); } appendRow(it); } void ProjectChangesModel::removeProject(IProject* p) { QStandardItem* it=projectItem(p); if (!it) { // when the project is closed before it was fully populated, we won't ever see a // projectOpened signal - handle this gracefully by just ignoring the remove request return; } removeRow(it->row()); } QStandardItem* findItemChild(QStandardItem* parent, const QVariant& value, int role = Qt::DisplayRole) { for(int i=0; irowCount(); i++) { QStandardItem* curr=parent->child(i); if(curr->data(role) == value) return curr; } return nullptr; } QStandardItem* ProjectChangesModel::projectItem(IProject* p) const { return findItemChild(invisibleRootItem(), p->name(), ProjectChangesModel::ProjectNameRole); } void ProjectChangesModel::updateState(IProject* p, const KDevelop::VcsStatusInfo& status) { QStandardItem* pItem = projectItem(p); Q_ASSERT(pItem); VcsFileChangesModel::updateState(pItem, status); } void ProjectChangesModel::changes(IProject* project, const QList& urls, IBasicVersionControl::RecursionMode mode) { IPlugin* vcsplugin=project->versionControlPlugin(); IBasicVersionControl* vcs = vcsplugin ? vcsplugin->extension() : nullptr; if(vcs && vcs->isVersionControlled(urls.first())) { //TODO: filter? VcsJob* job=vcs->status(urls, mode); job->setProperty("urls", QVariant::fromValue>(urls)); job->setProperty("mode", QVariant::fromValue(mode)); job->setProperty("project", QVariant::fromValue(project)); connect(job, &VcsJob::finished, this, &ProjectChangesModel::statusReady); ICore::self()->runController()->registerJob(job); } } void ProjectChangesModel::statusReady(KJob* job) { auto* status=static_cast(job); const QList states = status->fetchResults().toList(); auto* project = job->property("project").value(); if(!project) return; QSet foundUrls; foundUrls.reserve(states.size()); for (const QVariant& state : states) { const VcsStatusInfo st = state.value(); foundUrls += st.url(); updateState(project, st); } QStandardItem* itProject = projectItem(project); if (!itProject) { qCDebug(PROJECT) << "Project no longer listed in model:" << project->name() << "- skipping update"; return; } IBasicVersionControl::RecursionMode mode = IBasicVersionControl::RecursionMode(job->property("mode").toInt()); - const QSet uncertainUrls = urls(itProject).toSet().subtract(foundUrls); + const QList projectUrls = urls(itProject); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const QSet uncertainUrls = QSet(projectUrls.begin(), projectUrls.end()).subtract(foundUrls); +#else + const QSet uncertainUrls = projectUrls.toSet().subtract(foundUrls); +#endif const QList sourceUrls = job->property("urls").value>(); for (const QUrl& url : sourceUrls) { if(url.isLocalFile() && QDir(url.toLocalFile()).exists()) { for (const QUrl& currentUrl : uncertainUrls) { if((mode == IBasicVersionControl::NonRecursive && currentUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) == url.adjusted(QUrl::StripTrailingSlash)) || (mode == IBasicVersionControl::Recursive && url.isParentOf(currentUrl)) ) { removeUrl(currentUrl); } } } } } void ProjectChangesModel::documentSaved(KDevelop::IDocument* document) { reload({document->url()}); } void ProjectChangesModel::itemsAdded(const QModelIndex& parent, int start, int end) { ProjectModel* model=ICore::self()->projectController()->projectModel(); ProjectBaseItem* item=model->itemFromIndex(parent); if(!item) return; IProject* project=item->project(); if(!project) return; QList urls; for(int i=start; iindex(i, 0, parent); item=model->itemFromIndex(idx); if(item->type()==ProjectBaseItem::File || item->type()==ProjectBaseItem::Folder || item->type()==ProjectBaseItem::BuildFolder) urls += item->path().toUrl(); } if(!urls.isEmpty()) changes(project, urls, KDevelop::IBasicVersionControl::NonRecursive); } void ProjectChangesModel::reload(const QList& projects) { for (IProject* project : projects) { changes(project, {project->path().toUrl()}, KDevelop::IBasicVersionControl::Recursive); } } void ProjectChangesModel::reload(const QList& urls) { for (const QUrl& url : urls) { IProject* project=ICore::self()->projectController()->findProjectForUrl(url); if (project) { // FIXME: merge multiple urls of the same project changes(project, {url}, KDevelop::IBasicVersionControl::NonRecursive); } } } void ProjectChangesModel::reloadAll() { QList< IProject* > projects = ICore::self()->projectController()->projects(); reload(projects); } void ProjectChangesModel::jobUnregistered(KJob* job) { static const std::array readOnly = { KDevelop::VcsJob::Add, KDevelop::VcsJob::Remove, KDevelop::VcsJob::Pull, KDevelop::VcsJob::Commit, KDevelop::VcsJob::Move, KDevelop::VcsJob::Copy, KDevelop::VcsJob::Revert, }; auto* vcsjob = qobject_cast(job); if (vcsjob && std::find(readOnly.begin(), readOnly.end(), vcsjob->type()) != readOnly.end()) { reloadAll(); } } void ProjectChangesModel::repositoryBranchChanged(const QUrl& url) { IProject* project = ICore::self()->projectController()->findProjectForUrl(url); if(project) { IPlugin* v = project->versionControlPlugin(); Q_ASSERT(v); auto* branching = v->extension(); Q_ASSERT(branching); VcsJob* job = branching->currentBranch(url); connect(job, &VcsJob::resultsReady, this, &ProjectChangesModel::branchNameReady); job->setProperty("project", QVariant::fromValue(project)); ICore::self()->runController()->registerJob(job); } } void ProjectChangesModel::branchNameReady(VcsJob* job) { auto* project = qobject_cast(job->property("project").value()); if(job->status()==VcsJob::JobSucceeded) { QString name = job->fetchResults().toString(); QString branchName = name.isEmpty() ? i18n("no branch") : name; projectItem(project)->setText(i18nc("project name (branch name)", "%1 (%2)", project->name(), branchName)); } else { projectItem(project)->setText(project->name()); } reload(QList() << project); } diff --git a/kdevplatform/serialization/tests/bench_itemrepository.cpp b/kdevplatform/serialization/tests/bench_itemrepository.cpp index 4151e9c53e..9e3f7bd432 100644 --- a/kdevplatform/serialization/tests/bench_itemrepository.cpp +++ b/kdevplatform/serialization/tests/bench_itemrepository.cpp @@ -1,224 +1,228 @@ /* * This file is part of KDevelop * Copyright 2012-2013 Milian Wolff * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bench_itemrepository.h" #include #include #include #include QTEST_GUILESS_MAIN(BenchItemRepository) using namespace KDevelop; struct TestData { uint length; TestData& operator=(const TestData& rhs) = delete; uint itemSize() const { return sizeof(TestData) + length; } uint hash() const { const char* str = (( const char* )this) + sizeof(TestData); return IndexedString::hashString(str, length); } }; struct TestDataRepositoryItemRequest { //The text is supposed to be utf8 encoded TestDataRepositoryItemRequest(const char* text, uint length) : m_length(length) , m_text(text) , m_hash(IndexedString::hashString(text, length)) { } enum { AverageSize = 10 //This should be the approximate average size of an Item }; using HashType = uint; //Should return the hash-value associated with this request(For example the hash of a string) HashType hash() const { return m_hash; } //Should return the size of an item created with createItem uint itemSize() const { return sizeof(TestData) + m_length; } //Should create an item where the information of the requested item is permanently stored. The pointer //@param item equals an allocated range with the size of itemSize(). void createItem(TestData* item) const { item->length = m_length; void* itemText = reinterpret_cast(item + 1); memcpy(itemText, m_text, m_length); } static void destroy(TestData* item, AbstractItemRepository&) { Q_UNUSED(item); //Nothing to do here (The object is not intelligent) } static bool persistent(const TestData* item) { Q_UNUSED(item); return true; } //Should return whether the here requested item equals the given item bool equals(const TestData* item) const { return item->length == m_length && (memcmp(++item, m_text, m_length) == 0); } unsigned short m_length; const char* m_text; unsigned int m_hash; }; using TestDataRepository = ItemRepository; void BenchItemRepository::initTestCase() { ItemRepositoryRegistry::initialize(m_repositoryPath); } void BenchItemRepository::cleanupTestCase() { ItemRepositoryRegistry::deleteRepositoryFromDisk(m_repositoryPath); } static QVector generateData() { QVector data; static const int NUM_ITEMS = 100000; data.resize(NUM_ITEMS); for (int i = 0; i < NUM_ITEMS; ++i) { data[i] = QStringLiteral("/foo/%1").arg(i); } return data; } static QVector insertData(const QVector& data, TestDataRepository& repo) { QVector indices; indices.reserve(data.size()); for (const QString& item : data) { const QByteArray byteArray = item.toUtf8(); indices << repo.index(TestDataRepositoryItemRequest(byteArray.constData(), byteArray.length())); } return indices; } void BenchItemRepository::insert() { TestDataRepository repo("TestDataRepositoryInsert"); const QVector data = generateData(); QVector indices; QBENCHMARK_ONCE { indices = insertData(data, repo); repo.store(); } Q_ASSERT(indices.size() == data.size()); QCOMPARE(repo.statistics().totalItems, uint(data.size())); } void BenchItemRepository::remove() { TestDataRepository repo("TestDataRepositoryRemove"); const QVector data = generateData(); const QVector indices = insertData(data, repo); repo.store(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QVERIFY(indices.size() == QSet(indices.begin(), indices.end()).size()); +#else QVERIFY(indices.size() == indices.toList().toSet().size()); +#endif QVERIFY(indices.size() == data.size()); QBENCHMARK_ONCE { for (uint index : indices) { repo.deleteItem(index); } repo.store(); } QCOMPARE(repo.statistics().totalItems, 0u); } void BenchItemRepository::removeDisk() { const QVector data = generateData(); QVector indices; { TestDataRepository repo("TestDataRepositoryRemoveDisk"); indices = insertData(data, repo); repo.store(); } TestDataRepository repo("TestDataRepositoryRemoveDisk"); QVERIFY(repo.statistics().totalItems == static_cast(data.size())); QBENCHMARK_ONCE { for (uint index : qAsConst(indices)) { repo.deleteItem(index); } repo.store(); } QCOMPARE(repo.statistics().totalItems, 0u); } void BenchItemRepository::lookupKey() { TestDataRepository repo("TestDataRepositoryLookupKey"); const QVector data = generateData(); QVector indices = insertData(data, repo); srand(0); std::random_shuffle(indices.begin(), indices.end()); QBENCHMARK { for (uint index : qAsConst(indices)) { repo.itemFromIndex(index); } } } void BenchItemRepository::lookupValue() { TestDataRepository repo("TestDataRepositoryLookupValue"); const QVector data = generateData(); QVector indices = insertData(data, repo); srand(0); std::random_shuffle(indices.begin(), indices.end()); QBENCHMARK { for (const QString& item : data) { const QByteArray byteArray = item.toUtf8(); repo.findIndex(TestDataRepositoryItemRequest(byteArray.constData(), byteArray.length())); } } } diff --git a/kdevplatform/shell/plugincontroller.cpp b/kdevplatform/shell/plugincontroller.cpp index c7ac8aca4c..bbfeb73dff 100644 --- a/kdevplatform/shell/plugincontroller.cpp +++ b/kdevplatform/shell/plugincontroller.cpp @@ -1,868 +1,883 @@ /* This file is part of the KDE project Copyright 2004, 2007 Alexander Dymo Copyright 2006 Matt Rogers Based on code from Kopete Copyright (c) 2002-2003 Martijn Klingens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugincontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "shellextension.h" #include "runcontroller.h" #include "debugcontroller.h" #include "documentationcontroller.h" #include "sourceformattercontroller.h" #include "projectcontroller.h" #include "ktexteditorpluginintegration.h" #include "debug.h" namespace { inline QString KEY_Plugins() { return QStringLiteral("Plugins"); } inline QString KEY_Suffix_Enabled() { return QStringLiteral("Enabled"); } inline QString KEY_LoadMode() { return QStringLiteral("X-KDevelop-LoadMode"); } inline QString KEY_Category() { return QStringLiteral("X-KDevelop-Category"); } inline QString KEY_Mode() { return QStringLiteral("X-KDevelop-Mode"); } inline QString KEY_Version() { return QStringLiteral("X-KDevelop-Version"); } inline QString KEY_Interfaces() { return QStringLiteral("X-KDevelop-Interfaces"); } inline QString KEY_Required() { return QStringLiteral("X-KDevelop-IRequired"); } inline QString KEY_Optional() { return QStringLiteral("X-KDevelop-IOptional"); } inline QString KEY_KPlugin() { return QStringLiteral("KPlugin"); } inline QString KEY_EnabledByDefault() { return QStringLiteral("EnabledByDefault"); } inline QString KEY_Global() { return QStringLiteral("Global"); } inline QString KEY_Project() { return QStringLiteral("Project"); } inline QString KEY_Gui() { return QStringLiteral("GUI"); } inline QString KEY_AlwaysOn() { return QStringLiteral("AlwaysOn"); } inline QString KEY_UserSelectable() { return QStringLiteral("UserSelectable"); } bool isUserSelectable( const KPluginMetaData& info ) { QString loadMode = info.value(KEY_LoadMode()); return loadMode.isEmpty() || loadMode == KEY_UserSelectable(); } bool isGlobalPlugin( const KPluginMetaData& info ) { return info.value(KEY_Category()) == KEY_Global(); } bool hasMandatoryProperties( const KPluginMetaData& info ) { QString mode = info.value(KEY_Mode()); if (mode.isEmpty()) { return false; } // when the plugin is installed into the versioned plugin path, it's good to go if (info.fileName().contains(QLatin1String("/kdevplatform/" QT_STRINGIFY(KDEVELOP_PLUGIN_VERSION) "/"))) { return true; } // the version property is only required when the plugin is not installed into the right directory QVariant version = info.rawData().value(KEY_Version()).toVariant(); if (version.isValid() && version.value() == KDEVELOP_PLUGIN_VERSION) { return true; } return false; } +inline QSet stringSet(const QVariant& variant) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const QStringList list = variant.toStringList(); + return QSet(list.begin(), list.end()); +#else + return variant.toStringList().toSet(); +#endif +} + bool constraintsMatch( const KPluginMetaData& info, const QVariantMap& constraints) { for (auto it = constraints.begin(); it != constraints.end(); ++it) { const auto property = info.rawData().value(it.key()).toVariant(); if (!property.isValid()) { return false; } else if (property.canConvert()) { - QSet values = property.toStringList().toSet(); - QSet expected = it.value().toStringList().toSet(); + const QSet values = stringSet(property); + const QSet expected = stringSet(it.value()); if (!values.contains(expected)) { return false; } } else if (it.value() != property) { return false; } } return true; } struct Dependency { explicit Dependency(const QString &dependency) : interface(dependency) { if (dependency.contains(QLatin1Char('@'))) { const auto list = dependency.split(QLatin1Char('@'), QString::SkipEmptyParts); if (list.size() == 2) { interface = list.at(0); pluginName = list.at(1); } } } QString interface; QString pluginName; }; } namespace KDevelop { class PluginControllerPrivate { public: explicit PluginControllerPrivate(Core *core) : core(core) {} QVector plugins; //map plugin infos to currently loaded plugins using InfoToPluginMap = QHash; InfoToPluginMap loadedPlugins; // The plugin manager's mode. The mode is StartingUp until loadAllPlugins() // has finished loading the plugins, after which it is set to Running. // ShuttingDown and DoneShutdown are used during shutdown by the // async unloading of plugins. enum CleanupMode { Running /**< the plugin manager is running */, CleaningUp /**< the plugin manager is cleaning up for shutdown */, CleanupDone /**< the plugin manager has finished cleaning up */ }; CleanupMode cleanupMode; bool canUnload(const KPluginMetaData& plugin) { qCDebug(SHELL) << "checking can unload for:" << plugin.name() << plugin.value(KEY_LoadMode()); if (plugin.value(KEY_LoadMode()) == KEY_AlwaysOn()) { return false; } const QStringList interfaces = KPluginMetaData::readStringList(plugin.rawData(), KEY_Interfaces()); qCDebug(SHELL) << "checking dependencies:" << interfaces; for (auto it = loadedPlugins.constBegin(), end = loadedPlugins.constEnd(); it != end; ++it) { const KPluginMetaData& info = it.key(); if (info.pluginId() != plugin.pluginId()) { const QStringList dependencies = KPluginMetaData::readStringList(plugin.rawData(), KEY_Required()) + KPluginMetaData::readStringList(plugin.rawData(), KEY_Optional()); for (const QString& dep : dependencies) { Dependency dependency(dep); if (!dependency.pluginName.isEmpty() && dependency.pluginName != plugin.pluginId()) { continue; } if (interfaces.contains(dependency.interface) && !canUnload(info)) { return false; } } } } return true; } KPluginMetaData infoForId( const QString& id ) const { for (const KPluginMetaData& info : plugins) { if (info.pluginId() == id) { return info; } } return KPluginMetaData(); } /** * Iterate over all cached plugin infos, and call the functor for every enabled plugin. * * If an extension and/or pluginName is given, the functor will only be called for * those plugins matching this information. * * The functor should return false when the iteration can be stopped, and true if it * should be continued. */ template void foreachEnabledPlugin(F func, const QString &extension = {}, const QVariantMap& constraints = QVariantMap(), const QString &pluginName = {}) const { const auto currentPlugins = plugins; for (const auto& info : currentPlugins) { if ((pluginName.isEmpty() || info.pluginId() == pluginName) && (extension.isEmpty() || KPluginMetaData::readStringList(info.rawData(), KEY_Interfaces()).contains(extension)) && constraintsMatch(info, constraints) && isEnabled(info)) { if (!func(info)) { break; } } } } enum EnableState { DisabledByEnv, DisabledBySetting, DisabledByUnknown, FirstEnabledState, EnabledBySetting = FirstEnabledState, AlwaysEnabled }; /** * Estimate enabled state of a plugin */ EnableState enabledState(const KPluginMetaData& info) const { // first check black listing from environment static const QStringList disabledPlugins = QString::fromLatin1(qgetenv("KDEV_DISABLE_PLUGINS")).split(QLatin1Char(';')); if (disabledPlugins.contains(info.pluginId())) { return DisabledByEnv; } if (!isUserSelectable( info )) return AlwaysEnabled; // read stored user preference const KConfigGroup grp = Core::self()->activeSession()->config()->group( KEY_Plugins() ); const QString pluginEnabledKey = info.pluginId() + KEY_Suffix_Enabled(); if (grp.hasKey(pluginEnabledKey)) { return grp.readEntry(pluginEnabledKey, true) ? EnabledBySetting : DisabledBySetting; } // should not happen return DisabledByUnknown; } /** * Decide whether a plugin is enabled */ bool isEnabled(const KPluginMetaData& info) const { return (enabledState(info) >= FirstEnabledState); } Core* const core; }; PluginController::PluginController(Core *core) : IPluginController() , d_ptr(new PluginControllerPrivate(core)) { Q_D(PluginController); setObjectName(QStringLiteral("PluginController")); QSet foundPlugins; auto newPlugins = KPluginLoader::findPlugins(QStringLiteral("kdevplatform/" QT_STRINGIFY(KDEVELOP_PLUGIN_VERSION)), [&](const KPluginMetaData& meta) { if (meta.serviceTypes().contains(QStringLiteral("KDevelop/Plugin"))) { foundPlugins.insert(meta.pluginId()); return true; } else { qCWarning(SHELL) << "Plugin" << meta.fileName() << "is installed into the kdevplatform plugin directory, but does not have" " \"KDevelop/Plugin\" set as the service type. This plugin will not be loaded."; return false; } }); qCDebug(SHELL) << "Found" << newPlugins.size() << "plugins:" << foundPlugins; if (newPlugins.isEmpty()) { qCWarning(SHELL) << "Did not find any plugins, check your environment."; qCWarning(SHELL) << " Note: QT_PLUGIN_PATH is set to:" << qgetenv("QT_PLUGIN_PATH"); } d->plugins = newPlugins; KTextEditorIntegration::initialize(); const QVector ktePlugins = KPluginLoader::findPlugins(QStringLiteral("ktexteditor"), [](const KPluginMetaData & md) { return md.serviceTypes().contains(QStringLiteral("KTextEditor/Plugin")) && md.serviceTypes().contains(QStringLiteral("KDevelop/Plugin")); }); foundPlugins.clear(); std::for_each(ktePlugins.cbegin(), ktePlugins.cend(), [&foundPlugins](const KPluginMetaData& data) { foundPlugins << data.pluginId(); }); qCDebug(SHELL) << "Found" << ktePlugins.size() << " KTextEditor plugins:" << foundPlugins; d->plugins.reserve(d->plugins.size() + ktePlugins.size()); for (const auto& info : ktePlugins) { auto data = info.rawData(); // temporary workaround for Kate's ctags plugin being enabled by default // see https://mail.kde.org/pipermail/kwrite-devel/2019-July/004821.html if (info.pluginId() == QLatin1String("katectagsplugin")) { auto kpluginData = data[KEY_KPlugin()].toObject(); kpluginData[KEY_EnabledByDefault()] = false; data[KEY_KPlugin()] = kpluginData; } // add some KDevelop specific JSON data data[KEY_Category()] = KEY_Global(); data[KEY_Mode()] = KEY_Gui(); data[KEY_Version()] = KDEVELOP_PLUGIN_VERSION; d->plugins.append({data, info.fileName(), info.metaDataFileName()}); } d->cleanupMode = PluginControllerPrivate::Running; // Register the KDevelop::IPlugin* metatype so we can properly unload it qRegisterMetaType( "KDevelop::IPlugin*" ); } PluginController::~PluginController() { Q_D(PluginController); if ( d->cleanupMode != PluginControllerPrivate::CleanupDone ) { qCWarning(SHELL) << "Destructing plugin controller without going through the shutdown process!"; } } KPluginMetaData PluginController::pluginInfo( const IPlugin* plugin ) const { Q_D(const PluginController); return d->loadedPlugins.key(const_cast(plugin)); } void PluginController::cleanup() { Q_D(PluginController); if(d->cleanupMode != PluginControllerPrivate::Running) { //qCDebug(SHELL) << "called when not running. state =" << d->cleanupMode; return; } d->cleanupMode = PluginControllerPrivate::CleaningUp; // Ask all plugins to unload while ( !d->loadedPlugins.isEmpty() ) { //Let the plugin do some stuff before unloading unloadPlugin(d->loadedPlugins.begin().value(), Now); } d->cleanupMode = PluginControllerPrivate::CleanupDone; } IPlugin* PluginController::loadPlugin( const QString& pluginName ) { return loadPluginInternal( pluginName ); } void PluginController::initialize() { Q_D(PluginController); QElapsedTimer timer; timer.start(); QMap pluginMap; if( ShellExtension::getInstance()->defaultPlugins().isEmpty() ) { for (const KPluginMetaData& pi : qAsConst(d->plugins)) { QJsonValue enabledByDefaultValue = pi.rawData()[KEY_KPlugin()].toObject()[KEY_EnabledByDefault()]; // plugins enabled until explicitly specified otherwise const bool enabledByDefault = (enabledByDefaultValue.isNull() || enabledByDefaultValue.toBool()); pluginMap.insert(pi.pluginId(), enabledByDefault); } } else { // Get the default from the ShellExtension const auto defaultPlugins = ShellExtension::getInstance()->defaultPlugins(); for (const QString& s : defaultPlugins) { pluginMap.insert( s, true ); } } KConfigGroup grp = Core::self()->activeSession()->config()->group( KEY_Plugins() ); QMap entries = grp.entryMap(); QMap::Iterator it; for ( it = entries.begin(); it != entries.end(); ++it ) { const QString key = it.key(); if (key.endsWith(KEY_Suffix_Enabled())) { const QString pluginid = key.left(key.length() - 7); const bool defValue = pluginMap.value( pluginid, false ); const bool enabled = grp.readEntry(key, defValue); pluginMap.insert( pluginid, enabled ); } } // store current known set of enabled plugins for (const KPluginMetaData& pi : qAsConst(d->plugins)) { if (isUserSelectable(pi)) { auto it = pluginMap.constFind(pi.pluginId()); if (it != pluginMap.constEnd() && (it.value())) { grp.writeEntry(pi.pluginId() + KEY_Suffix_Enabled(), true); } } else { // Backward compat: Remove any now-obsolete entries grp.deleteEntry(pi.pluginId() + QLatin1String("Disabled")); } } // Synchronize so we're writing out to the file. grp.sync(); // load global plugins for (const KPluginMetaData& pi : qAsConst(d->plugins)) { if (isGlobalPlugin(pi)) { loadPluginInternal(pi.pluginId()); } } qCDebug(SHELL) << "Done loading plugins - took:" << timer.elapsed() << "ms"; } QList PluginController::loadedPlugins() const { Q_D(const PluginController); return d->loadedPlugins.values(); } bool PluginController::unloadPlugin( const QString & pluginId ) { Q_D(PluginController); IPlugin *thePlugin = plugin( pluginId ); bool canUnload = d->canUnload( d->infoForId( pluginId ) ); qCDebug(SHELL) << "Unloading plugin:" << pluginId << "?" << thePlugin << canUnload; if( thePlugin && canUnload ) { return unloadPlugin(thePlugin, Later); } return (canUnload && thePlugin); } bool PluginController::unloadPlugin(IPlugin* plugin, PluginDeletion deletion) { Q_D(PluginController); qCDebug(SHELL) << "unloading plugin:" << plugin << pluginInfo( plugin ).name(); emit unloadingPlugin(plugin); plugin->unload(); emit pluginUnloaded(plugin); //Remove the plugin from our list of plugins so we create a new //instance when we're asked for it again. //This is important to do right here, not later when the plugin really //vanishes. For example project re-opening might try to reload the plugin //and then would get the "old" pointer which will be deleted in the next //event loop run and thus causing crashes. for ( PluginControllerPrivate::InfoToPluginMap::Iterator it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) { if ( it.value() == plugin ) { d->loadedPlugins.erase( it ); break; } } if (deletion == Later) plugin->deleteLater(); else delete plugin; return true; } KPluginMetaData PluginController::infoForPluginId( const QString &pluginId ) const { Q_D(const PluginController); auto it = std::find_if(d->plugins.constBegin(), d->plugins.constEnd(), [&](const KPluginMetaData& info) { return (info.pluginId() == pluginId); }); return (it != d->plugins.constEnd()) ? *it : KPluginMetaData(); } IPlugin *PluginController::loadPluginInternal( const QString &pluginId ) { Q_D(PluginController); QElapsedTimer timer; timer.start(); KPluginMetaData info = infoForPluginId( pluginId ); if ( !info.isValid() ) { qCWarning(SHELL) << "Unable to find a plugin named '" << pluginId << "'!" ; return nullptr; } if ( IPlugin* plugin = d->loadedPlugins.value( info ) ) { return plugin; } const auto enabledState = d->enabledState(info); if (enabledState < PluginControllerPrivate::FirstEnabledState) { // Do not load disabled plugins qCDebug(SHELL) << "Not loading plugin named" << pluginId << ( (enabledState == PluginControllerPrivate::DisabledByEnv) ? "because disabled by KDEV_DISABLE_PLUGINS." : (enabledState == PluginControllerPrivate::DisabledBySetting) ? "because disabled by setting." : /* else, should not happen */ "because disabled for unknown reason."); return nullptr; } if ( !hasMandatoryProperties( info ) ) { qCWarning(SHELL) << "Unable to load plugin named" << pluginId << "because not all mandatory properties are set."; return nullptr; } if ( info.value(KEY_Mode()) == KEY_Gui() && Core::self()->setupFlags() == Core::NoUi ) { qCDebug(SHELL) << "Not loading plugin named" << pluginId << "- Running in No-Ui mode, but the plugin says it needs a GUI"; return nullptr; } qCDebug(SHELL) << "Attempting to load" << pluginId << "- name:" << info.name(); emit loadingPlugin( info.pluginId() ); // first, ensure all dependencies are available and not disabled // this is unrelated to whether they are loaded already or not. // when we depend on e.g. A and B, but B cannot be found, then we // do not want to load A first and then fail on B and leave A loaded. // this would happen if we'd skip this step here and directly loadDependencies. QStringList missingInterfaces; if ( !hasUnresolvedDependencies( info, missingInterfaces ) ) { qCWarning(SHELL) << "Can't load plugin" << pluginId << "some of its required dependencies could not be fulfilled:" << missingInterfaces.join(QLatin1Char(',')); return nullptr; } // now ensure all dependencies are loaded QString failedDependency; if( !loadDependencies( info, failedDependency ) ) { qCWarning(SHELL) << "Can't load plugin" << pluginId << "because a required dependency could not be loaded:" << failedDependency; return nullptr; } // same for optional dependencies, but don't error out if anything fails loadOptionalDependencies( info ); // now we can finally load the plugin itself KPluginLoader loader(info.fileName()); auto factory = loader.factory(); if (!factory) { qCWarning(SHELL) << "Can't load plugin" << pluginId << "because a factory to load the plugin could not be obtained:" << loader.errorString(); return nullptr; } // now create it auto plugin = factory->create(d->core); if (!plugin) { if (auto katePlugin = factory->create(d->core, QVariantList() << info.pluginId())) { plugin = new KTextEditorIntegration::Plugin(katePlugin, d->core); } else { qCWarning(SHELL) << "Creating plugin" << pluginId << "failed."; return nullptr; } } KConfigGroup group = Core::self()->activeSession()->config()->group(KEY_Plugins()); // runtime errors such as missing executables on the system or such get checked now if (plugin->hasError()) { qCWarning(SHELL) << "Could not load plugin" << pluginId << ", it reported the error:" << plugin->errorDescription() << "Disabling the plugin now."; group.writeEntry(info.pluginId() + KEY_Suffix_Enabled(), false); // do the same as KPluginInfo did group.sync(); unloadPlugin(pluginId); return nullptr; } // yay, it all worked - the plugin is loaded d->loadedPlugins.insert(info, plugin); group.writeEntry(info.pluginId() + KEY_Suffix_Enabled(), true); // do the same as KPluginInfo did group.sync(); qCDebug(SHELL) << "Successfully loaded plugin" << pluginId << "from" << loader.fileName() << "- took:" << timer.elapsed() << "ms"; emit pluginLoaded( plugin ); return plugin; } IPlugin* PluginController::plugin(const QString& pluginId) const { Q_D(const PluginController); KPluginMetaData info = infoForPluginId( pluginId ); if ( !info.isValid() ) return nullptr; return d->loadedPlugins.value( info ); } bool PluginController::hasUnresolvedDependencies( const KPluginMetaData& info, QStringList& missing ) const { Q_D(const PluginController); - QSet required = KPluginMetaData::readStringList(info.rawData(), KEY_Required()).toSet(); + const QStringList requiredList = KPluginMetaData::readStringList(info.rawData(), KEY_Required()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QSet required(requiredList.begin(), requiredList.end()); +#else + QSet required = requiredList.toSet(); +#endif if (!required.isEmpty()) { d->foreachEnabledPlugin([&required] (const KPluginMetaData& plugin) -> bool { const auto interfaces = KPluginMetaData::readStringList(plugin.rawData(), KEY_Interfaces()); for (const QString& iface : interfaces) { required.remove(iface); required.remove(iface + QLatin1Char('@') + plugin.pluginId()); } return !required.isEmpty(); }); } // if we found all dependencies required should be empty now if (!required.isEmpty()) { missing = required.values(); return false; } return true; } void PluginController::loadOptionalDependencies( const KPluginMetaData& info ) { const QStringList dependencies = KPluginMetaData::readStringList(info.rawData(), KEY_Optional()); for (const QString& dep : dependencies) { Dependency dependency(dep); if (!pluginForExtension(dependency.interface, dependency.pluginName)) { qCDebug(SHELL) << "Couldn't load optional dependency:" << dep << info.pluginId(); } } } bool PluginController::loadDependencies( const KPluginMetaData& info, QString& failedDependency ) { const QStringList dependencies = KPluginMetaData::readStringList(info.rawData(), KEY_Required()); for (const QString& value : dependencies) { Dependency dependency(value); if (!pluginForExtension(dependency.interface, dependency.pluginName)) { failedDependency = value; return false; } } return true; } IPlugin *PluginController::pluginForExtension(const QString &extension, const QString &pluginName, const QVariantMap& constraints) { Q_D(PluginController); IPlugin* plugin = nullptr; d->foreachEnabledPlugin([this, &plugin] (const KPluginMetaData& info) -> bool { Q_D(PluginController); plugin = d->loadedPlugins.value( info ); if( !plugin ) { plugin = loadPluginInternal( info.pluginId() ); } return !plugin; }, extension, constraints, pluginName); return plugin; } QList PluginController::allPluginsForExtension(const QString &extension, const QVariantMap& constraints) { Q_D(PluginController); //qCDebug(SHELL) << "Finding all Plugins for Extension:" << extension << "|" << constraints; QList plugins; d->foreachEnabledPlugin([this, &plugins] (const KPluginMetaData& info) -> bool { Q_D(PluginController); IPlugin* plugin = d->loadedPlugins.value( info ); if( !plugin) { plugin = loadPluginInternal( info.pluginId() ); } if (plugin && !plugins.contains(plugin)) { plugins << plugin; } return true; }, extension, constraints); return plugins; } QVector PluginController::queryExtensionPlugins(const QString& extension, const QVariantMap& constraints) const { Q_D(const PluginController); QVector plugins; d->foreachEnabledPlugin([&plugins] (const KPluginMetaData& info) -> bool { plugins << info; return true; }, extension, constraints); return plugins; } QStringList PluginController::allPluginNames() const { Q_D(const PluginController); QStringList names; names.reserve(d->plugins.size()); for (const KPluginMetaData& info : qAsConst(d->plugins)) { names << info.pluginId(); } return names; } QList PluginController::queryPluginsForContextMenuExtensions(KDevelop::Context* context, QWidget* parent) const { Q_D(const PluginController); // This fixes random order of extension menu items between different runs of KDevelop. // Without sorting we have random reordering of "Analyze With" submenu for example: // 1) "Cppcheck" actions, "Vera++" actions - first run // 2) "Vera++" actions, "Cppcheck" actions - some other run. QMultiMap sortedPlugins; for (auto it = d->loadedPlugins.constBegin(); it != d->loadedPlugins.constEnd(); ++it) { sortedPlugins.insert(it.key().name(), it.value()); } QList exts; exts.reserve(sortedPlugins.size()); for (IPlugin* plugin : qAsConst(sortedPlugins)) { exts << plugin->contextMenuExtension(context, parent); } exts << Core::self()->debugControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->documentationControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->sourceFormatterControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->runControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->projectControllerInternal()->contextMenuExtension(context, parent); return exts; } QStringList PluginController::projectPlugins() const { Q_D(const PluginController); QStringList names; for (const KPluginMetaData& info : qAsConst(d->plugins)) { if (info.value(KEY_Category()) == KEY_Project()) { names << info.pluginId(); } } return names; } void PluginController::loadProjectPlugins() { const auto pluginNames = projectPlugins(); for (const QString& name : pluginNames) { loadPluginInternal( name ); } } void PluginController::unloadProjectPlugins() { const auto pluginNames = projectPlugins(); for (const QString& name : pluginNames) { unloadPlugin( name ); } } QVector PluginController::allPluginInfos() const { Q_D(const PluginController); return d->plugins; } void PluginController::updateLoadedPlugins() { Q_D(PluginController); QStringList defaultPlugins = ShellExtension::getInstance()->defaultPlugins(); KConfigGroup grp = Core::self()->activeSession()->config()->group( KEY_Plugins() ); for (const KPluginMetaData& info : qAsConst(d->plugins)) { if( isGlobalPlugin( info ) ) { bool enabled = grp.readEntry(info.pluginId() + KEY_Suffix_Enabled(), ( defaultPlugins.isEmpty() || defaultPlugins.contains( info.pluginId() ) ) ) || !isUserSelectable( info ); bool loaded = d->loadedPlugins.contains( info ); if( loaded && !enabled ) { qCDebug(SHELL) << "unloading" << info.pluginId(); if( !unloadPlugin( info.pluginId() ) ) { grp.writeEntry( info.pluginId() + KEY_Suffix_Enabled(), false ); } } else if( !loaded && enabled ) { loadPluginInternal( info.pluginId() ); } } // TODO: what about project plugins? what about dependency plugins? } } void PluginController::resetToDefaults() { Q_D(PluginController); KSharedConfigPtr cfg = Core::self()->activeSession()->config(); cfg->deleteGroup( KEY_Plugins() ); cfg->sync(); KConfigGroup grp = cfg->group( KEY_Plugins() ); QStringList plugins = ShellExtension::getInstance()->defaultPlugins(); if( plugins.isEmpty() ) { for (const KPluginMetaData& info : qAsConst(d->plugins)) { if (!isUserSelectable(info)) { continue; } QJsonValue enabledByDefault = info.rawData()[KEY_KPlugin()].toObject()[KEY_EnabledByDefault()]; // plugins enabled until explicitly specified otherwise if (enabledByDefault.isNull() || enabledByDefault.toBool()) { plugins << info.pluginId(); } } } for (const QString& s : qAsConst(plugins)) { grp.writeEntry(s + KEY_Suffix_Enabled(), true); } grp.sync(); } } diff --git a/kdevplatform/shell/projectcontroller.cpp b/kdevplatform/shell/projectcontroller.cpp index 91435623a2..0911cc1700 100644 --- a/kdevplatform/shell/projectcontroller.cpp +++ b/kdevplatform/shell/projectcontroller.cpp @@ -1,1375 +1,1380 @@ /* This file is part of KDevelop Copyright 2006 Adam Treat Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectcontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" // TODO: Should get rid off this include (should depend on IProject only) #include "project.h" #include "mainwindow.h" #include "shellextension.h" #include "plugincontroller.h" #include "configdialog.h" #include "uicontroller.h" #include "documentcontroller.h" #include "openprojectdialog.h" #include "sessioncontroller.h" #include "session.h" #include "debug.h" namespace KDevelop { class ProjectControllerPrivate { public: QList m_projects; QMap< IProject*, QList > m_projectPlugins; QPointer m_recentProjectsAction; Core* const m_core; // IProject* m_currentProject; ProjectModel* const model; QPointer m_openProject; QPointer m_fetchProject; QPointer m_closeProject; QPointer m_openConfig; IProjectDialogProvider* dialog; QList m_currentlyOpening; // project-file urls that are being opened ProjectController* const q; ProjectBuildSetModel* buildset; bool m_foundProjectFile; //Temporary flag used while searching the hierarchy for a project file bool m_cleaningUp; //Temporary flag enabled while destroying the project-controller ProjectChangesModel* m_changesModel = nullptr; QHash< IProject*, QPointer > m_parseJobs; // parse jobs that add files from the project to the background parser. ProjectControllerPrivate(Core* core, ProjectController* p) : m_core(core) , model(new ProjectModel()) , dialog(nullptr) , q(p) , buildset(nullptr) , m_foundProjectFile(false) , m_cleaningUp(false) { } void unloadAllProjectPlugins() { if( m_projects.isEmpty() ) m_core->pluginControllerInternal()->unloadProjectPlugins(); } void projectConfig( QObject * obj ) { if( !obj ) return; auto* proj = qobject_cast(obj); if( !proj ) return; auto cfgDlg = new KDevelop::ConfigDialog(m_core->uiController()->activeMainWindow()); cfgDlg->setAttribute(Qt::WA_DeleteOnClose); cfgDlg->setModal(true); QVector configPages; ProjectConfigOptions options; options.developerFile = proj->developerFile(); options.developerTempFile = proj->developerTempFile(); options.projectTempFile = proj->projectTempFile(); options.project = proj; const auto plugins = findPluginsForProject(proj); for (IPlugin* plugin : plugins) { const int perProjectConfigPagesCount = plugin->perProjectConfigPages(); configPages.reserve(configPages.size() + perProjectConfigPagesCount); for (int i = 0; i < perProjectConfigPagesCount; ++i) { configPages.append(plugin->perProjectConfigPage(i, options, cfgDlg)); } } std::sort(configPages.begin(), configPages.end(), [](const ConfigPage* a, const ConfigPage* b) { return a->name() < b->name(); }); for (auto page : configPages) { cfgDlg->appendConfigPage(page); } QObject::connect(cfgDlg, &ConfigDialog::configSaved, cfgDlg, [this, proj](ConfigPage* page) { Q_UNUSED(page) Q_ASSERT_X(proj, Q_FUNC_INFO, "ConfigDialog signalled project config change, but no project set for configuring!"); emit q->projectConfigurationChanged(proj); }); cfgDlg->setWindowTitle(i18n("Configure Project %1", proj->name())); QObject::connect(cfgDlg, &KDevelop::ConfigDialog::finished, proj, [proj]() { proj->projectConfiguration()->sync(); }); cfgDlg->show(); } void saveListOfOpenedProjects() { auto activeSession = Core::self()->activeSession(); if (!activeSession) { return; } QList openProjects; openProjects.reserve( m_projects.size() ); for (IProject* project : qAsConst(m_projects)) { openProjects.append(project->projectFile().toUrl()); } activeSession->setContainedProjects( openProjects ); } // Recursively collects builder dependencies for a project. static void collectBuilders( QList< IProjectBuilder* >& destination, IProjectBuilder* topBuilder, IProject* project ) { const QList auxBuilders = topBuilder->additionalBuilderPlugins(project); destination.append( auxBuilders ); for (IProjectBuilder* auxBuilder : auxBuilders ) { collectBuilders( destination, auxBuilder, project ); } } QVector findPluginsForProject( IProject* project ) const { const QList plugins = m_core->pluginController()->loadedPlugins(); const IBuildSystemManager* const buildSystemManager = project->buildSystemManager(); QVector projectPlugins; QList buildersForKcm; // Important to also include the "top" builder for the project, so // projects with only one such builder are kept working. Otherwise the project config // dialog is empty for such cases. if (buildSystemManager) { buildersForKcm << buildSystemManager->builder(); collectBuilders( buildersForKcm, buildSystemManager->builder(), project ); } for (auto plugin : plugins) { auto info = m_core->pluginController()->pluginInfo(plugin); auto* manager = plugin->extension(); if( manager && manager != project->projectFileManager() ) { // current plugin is a manager but does not apply to given project, skip continue; } auto* builder = plugin->extension(); if ( builder && !buildersForKcm.contains( builder ) ) { continue; } // Do not show config pages for analyzer tools which need a buildSystemManager // TODO: turn into generic feature to disable plugin config pages which do not apply for a project if (!buildSystemManager) { const auto required = KPluginMetaData::readStringList(info.rawData(), QStringLiteral("X-KDevelop-IRequired")); if (required.contains(QLatin1String("org.kdevelop.IBuildSystemManager"))) { continue; } } qCDebug(SHELL) << "Using plugin" << info.pluginId() << "for project" << project->name(); projectPlugins << plugin; } return projectPlugins; } void updateActionStates() { // if only one project loaded, this is always our target int itemCount = (m_projects.size() == 1) ? 1 : 0; if (itemCount == 0) { // otherwise base on selection auto* itemContext = dynamic_cast(ICore::self()->selectionController()->currentSelection()); if (itemContext) { itemCount = itemContext->items().count(); } } m_openConfig->setEnabled(itemCount == 1); m_closeProject->setEnabled(itemCount > 0); } QSet selectedProjects() { QSet projects; // if only one project loaded, this is our target if (m_projects.count() == 1) { projects.insert(m_projects.at(0)); } else { // otherwise base on selection auto* ctx = dynamic_cast(ICore::self()->selectionController()->currentSelection()); if (ctx) { const auto items = ctx->items(); for (ProjectBaseItem* item : items) { projects.insert(item->project()); } } } return projects; } void openProjectConfig() { auto projects = selectedProjects(); if (projects.count() == 1) { q->configureProject(*projects.constBegin()); } } void closeSelectedProjects() { const auto projects = selectedProjects(); for (IProject* project : projects) { q->closeProject(project); } } void importProject(const QUrl& url_) { QUrl url(url_); if (url.isLocalFile()) { const QString path = QFileInfo(url.toLocalFile()).canonicalFilePath(); if (!path.isEmpty()) { url = QUrl::fromLocalFile(path); } } if ( !url.isValid() ) { const QString messageText = i18n("Invalid Location: %1", url.toDisplayString(QUrl::PreferLocalFile)); auto* message = new Sublime::Message(messageText, Sublime::Message::Error); ICore::self()->uiController()->postMessage(message); return; } if ( m_currentlyOpening.contains(url)) { qCDebug(SHELL) << "Already opening " << url << ". Aborting."; const QString messageText = i18n("Already opening %1, not opening again", url.toDisplayString(QUrl::PreferLocalFile)); auto* message = new Sublime::Message(messageText, Sublime::Message::Information); message->setAutoHide(0); ICore::self()->uiController()->postMessage(message); return; } const auto projects = m_projects; for (IProject* project : projects) { if ( url == project->projectFile().toUrl() ) { if ( dialog->userWantsReopen() ) { // close first, then open again by falling through q->closeProject(project); } else { // abort return; } } } m_currentlyOpening += url; m_core->pluginControllerInternal()->loadProjectPlugins(); auto* project = new Project(); QObject::connect(project, &Project::aboutToOpen, q, &ProjectController::projectAboutToBeOpened); if ( !project->open( Path(url) ) ) { m_currentlyOpening.removeAll(url); q->abortOpeningProject(project); project->deleteLater(); } } void areaChanged(Sublime::Area* area) { KActionCollection* ac = m_core->uiControllerInternal()->defaultMainWindow()->actionCollection(); ac->action(QStringLiteral("commit_current_project"))->setEnabled(area->objectName() == QLatin1String("code")); ac->action(QStringLiteral("commit_current_project"))->setVisible(area->objectName() == QLatin1String("code")); } }; IProjectDialogProvider::IProjectDialogProvider() {} IProjectDialogProvider::~IProjectDialogProvider() {} ProjectDialogProvider::ProjectDialogProvider(ProjectControllerPrivate* p) : d(p) {} ProjectDialogProvider::~ProjectDialogProvider() {} bool writeNewProjectFile( const QString& localConfigFile, const QString& name, const QString& createdFrom, const QString& manager ) { KSharedConfigPtr cfg = KSharedConfig::openConfig( localConfigFile, KConfig::SimpleConfig ); if (!cfg->isConfigWritable(true)) { qCDebug(SHELL) << "can't write to configfile"; return false; } KConfigGroup grp = cfg->group( "Project" ); grp.writeEntry( "Name", name ); grp.writeEntry( "CreatedFrom", createdFrom ); grp.writeEntry( "Manager", manager ); cfg->sync(); return true; } bool writeProjectSettingsToConfigFile(const QUrl& projectFileUrl, OpenProjectDialog* dlg) { if ( !projectFileUrl.isLocalFile() ) { QTemporaryFile tmp; if ( !tmp.open() ) { return false; } if ( !writeNewProjectFile( tmp.fileName(), dlg->projectName(), dlg->selectedUrl().fileName(), dlg->projectManager() ) ) { return false; } // explicitly close file before uploading it, see also: https://bugs.kde.org/show_bug.cgi?id=254519 tmp.close(); auto uploadJob = KIO::file_copy(QUrl::fromLocalFile(tmp.fileName()), projectFileUrl); KJobWidgets::setWindow(uploadJob, Core::self()->uiControllerInternal()->defaultMainWindow()); return uploadJob->exec(); } // Here and above we take .filename() part of the selectedUrl() to make it relative to the project root, // thus keeping .kdev file relocatable return writeNewProjectFile( projectFileUrl.toLocalFile(), dlg->projectName(), dlg->selectedUrl().fileName(), dlg->projectManager() ); } bool projectFileExists( const QUrl& u ) { if( u.isLocalFile() ) { return QFileInfo::exists( u.toLocalFile() ); } else { auto statJob = KIO::stat(u, KIO::StatJob::DestinationSide, 0, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, Core::self()->uiControllerInternal()->activeMainWindow()); return statJob->exec(); } } bool equalProjectFile( const QString& configPath, OpenProjectDialog* dlg ) { KSharedConfigPtr cfg = KSharedConfig::openConfig( configPath, KConfig::SimpleConfig ); KConfigGroup grp = cfg->group( "Project" ); QString defaultName = dlg->projectFileUrl().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).fileName(); return (grp.readEntry( "Name", QString() ) == dlg->projectName() || dlg->projectName() == defaultName) && grp.readEntry( "Manager", QString() ) == dlg->projectManager(); } QUrl ProjectDialogProvider::askProjectConfigLocation(bool fetch, const QUrl& startUrl, const QUrl& repoUrl, IPlugin* vcsOrProviderPlugin) { Q_ASSERT(d); ScopedDialog dlg(fetch, startUrl, repoUrl, vcsOrProviderPlugin, Core::self()->uiController()->activeMainWindow()); if(dlg->exec() == QDialog::Rejected) { return QUrl(); } QUrl projectFileUrl = dlg->projectFileUrl(); qCDebug(SHELL) << "selected project:" << projectFileUrl << dlg->projectName() << dlg->projectManager(); if ( dlg->projectManager() == QLatin1String("") ) { return projectFileUrl; } // controls if existing project file should be saved bool writeProjectConfigToFile = true; if( projectFileExists( projectFileUrl ) ) { // check whether config is equal bool shouldAsk = true; if( projectFileUrl == dlg->selectedUrl() ) { if( projectFileUrl.isLocalFile() ) { shouldAsk = !equalProjectFile( projectFileUrl.toLocalFile(), dlg ); } else { shouldAsk = false; QTemporaryFile tmpFile; if (tmpFile.open()) { auto downloadJob = KIO::file_copy(projectFileUrl, QUrl::fromLocalFile(tmpFile.fileName())); KJobWidgets::setWindow(downloadJob, qApp->activeWindow()); if (downloadJob->exec()) { shouldAsk = !equalProjectFile(tmpFile.fileName(), dlg); } } } } if ( shouldAsk ) { KGuiItem yes = KStandardGuiItem::yes(); yes.setText(i18n("Override")); yes.setToolTip(i18nc("@info:tooltip", "Continue to open the project and use the just provided project configuration.")); yes.setIcon(QIcon()); KGuiItem no = KStandardGuiItem::no(); no.setText(i18n("Open Existing File")); no.setToolTip(i18nc("@info:tooltip", "Continue to open the project but use the existing project configuration.")); no.setIcon(QIcon()); KGuiItem cancel = KStandardGuiItem::cancel(); cancel.setToolTip(i18nc("@info:tooltip", "Cancel and do not open the project.")); int ret = KMessageBox::questionYesNoCancel(qApp->activeWindow(), i18n("There already exists a project configuration file at %1.\n" "Do you want to override it or open the existing file?", projectFileUrl.toDisplayString(QUrl::PreferLocalFile)), i18n("Override existing project configuration"), yes, no, cancel ); if ( ret == KMessageBox::No ) { writeProjectConfigToFile = false; } else if ( ret == KMessageBox::Cancel ) { return QUrl(); } // else fall through and write new file } else { writeProjectConfigToFile = false; } } if (writeProjectConfigToFile) { Path projectConfigDir(projectFileUrl); projectConfigDir.setLastPathSegment(QStringLiteral(".kdev4")); auto delJob = KIO::del(projectConfigDir.toUrl()); delJob->exec(); if (!writeProjectSettingsToConfigFile(projectFileUrl, dlg)) { const QString messageText = i18n("Unable to create configuration file %1", projectFileUrl.url()); auto* message = new Sublime::Message(messageText, Sublime::Message::Error); ICore::self()->uiController()->postMessage(message); return QUrl(); } } return projectFileUrl; } bool ProjectDialogProvider::userWantsReopen() { Q_ASSERT(d); return (KMessageBox::questionYesNo( d->m_core->uiControllerInternal()->defaultMainWindow(), i18n( "Reopen the current project?" ) ) == KMessageBox::No) ? false : true; } void ProjectController::setDialogProvider(IProjectDialogProvider* dialog) { Q_D(ProjectController); Q_ASSERT(d->dialog); delete d->dialog; d->dialog = dialog; } ProjectController::ProjectController( Core* core ) : IProjectController(core) , d_ptr(new ProjectControllerPrivate(core, this)) { qRegisterMetaType>(); setObjectName(QStringLiteral("ProjectController")); //NOTE: this is required to be called here, such that the // actions are available when the UI controller gets // initialized *before* the project controller if (Core::self()->setupFlags() != Core::NoUi) { setupActions(); } } void ProjectController::setupActions() { Q_D(ProjectController); KActionCollection * ac = d->m_core->uiControllerInternal()->defaultMainWindow()->actionCollection(); QAction*action; d->m_openProject = action = ac->addAction( QStringLiteral("project_open") ); action->setText(i18nc( "@action", "Open / Import Project..." ) ); action->setToolTip( i18nc( "@info:tooltip", "Open or import project" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Open an existing KDevelop 4 project or import " "an existing Project into KDevelop 4. This entry " "allows one to select a KDevelop4 project file " "or an existing directory to open it in KDevelop. " "When opening an existing directory that does " "not yet have a KDevelop4 project file, the file " "will be created." ) ); action->setIcon(QIcon::fromTheme(QStringLiteral("project-open"))); connect(action, &QAction::triggered, this, [&] { openProject(); }); d->m_fetchProject = action = ac->addAction( QStringLiteral("project_fetch") ); action->setText(i18nc( "@action", "Fetch Project..." ) ); action->setIcon( QIcon::fromTheme( QStringLiteral("edit-download") ) ); action->setToolTip( i18nc( "@info:tooltip", "Fetch project" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Guides the user through the project fetch " "and then imports it into KDevelop 4." ) ); // action->setIcon(QIcon::fromTheme("project-open")); connect( action, &QAction::triggered, this, &ProjectController::fetchProject ); // action = ac->addAction( "project_close" ); // action->setText( i18n( "C&lose Project" ) ); // connect( action, SIGNAL(triggered(bool)), SLOT(closeProject()) ); // action->setToolTip( i18n( "Close project" ) ); // action->setWhatsThis( i18n( "Closes the current project." ) ); // action->setEnabled( false ); d->m_closeProject = action = ac->addAction( QStringLiteral("project_close") ); connect(action, &QAction::triggered, this, [this] { Q_D(ProjectController); d->closeSelectedProjects(); } ); action->setText( i18nc( "@action", "Close Project(s)" ) ); action->setIcon( QIcon::fromTheme( QStringLiteral("project-development-close") ) ); action->setToolTip( i18nc( "@info:tooltip", "Closes all currently selected projects" ) ); action->setEnabled( false ); d->m_openConfig = action = ac->addAction( QStringLiteral("project_open_config") ); connect(action, &QAction::triggered, this, [this] { Q_D(ProjectController); d->openProjectConfig(); } ); action->setText( i18n( "Open Configuration..." ) ); action->setIcon( QIcon::fromTheme(QStringLiteral("configure")) ); action->setEnabled( false ); action = ac->addAction( QStringLiteral("commit_current_project") ); connect( action, &QAction::triggered, this, &ProjectController::commitCurrentProject ); action->setText( i18n( "Commit Current Project..." ) ); action->setIconText( i18n( "Commit..." ) ); action->setIcon( QIcon::fromTheme(QStringLiteral("svn-commit")) ); connect(d->m_core->uiControllerInternal()->defaultMainWindow(), &MainWindow::areaChanged, this, [this] (Sublime::Area* area) { Q_D(ProjectController); d->areaChanged(area); }); d->m_core->uiControllerInternal()->area(0, QStringLiteral("code"))->addAction(action); KSharedConfig * config = KSharedConfig::openConfig().data(); // KConfigGroup group = config->group( "General Options" ); d->m_recentProjectsAction = KStandardAction::openRecent(this, SLOT(openProject(QUrl)), this); ac->addAction( QStringLiteral("project_open_recent"), d->m_recentProjectsAction ); d->m_recentProjectsAction->setText( i18n( "Open Recent Project" ) ); d->m_recentProjectsAction->setWhatsThis( i18nc( "@info:whatsthis", "Opens recently opened project." ) ); d->m_recentProjectsAction->loadEntries( KConfigGroup(config, "RecentProjects") ); auto* openProjectForFileAction = new QAction( this ); ac->addAction(QStringLiteral("project_open_for_file"), openProjectForFileAction); openProjectForFileAction->setText(i18n("Open Project for Current File")); openProjectForFileAction->setIcon(QIcon::fromTheme(QStringLiteral("project-open"))); connect( openProjectForFileAction, &QAction::triggered, this, &ProjectController::openProjectForUrlSlot); } ProjectController::~ProjectController() { Q_D(ProjectController); delete d->model; delete d->dialog; } void ProjectController::cleanup() { Q_D(ProjectController); if ( d->m_currentlyOpening.isEmpty() ) { d->saveListOfOpenedProjects(); } saveRecentProjectsActionEntries(); d->m_cleaningUp = true; if( buildSetModel() ) { buildSetModel()->storeToSession( Core::self()->activeSession() ); } closeAllProjects(); } void ProjectController::saveRecentProjectsActionEntries() { Q_D(ProjectController); if (!d->m_recentProjectsAction) return; auto config = KSharedConfig::openConfig(); KConfigGroup recentGroup = config->group("RecentProjects"); d->m_recentProjectsAction->saveEntries( recentGroup ); config->sync(); } void ProjectController::initialize() { Q_D(ProjectController); d->buildset = new ProjectBuildSetModel( this ); buildSetModel()->loadFromSession( Core::self()->activeSession() ); connect( this, &ProjectController::projectOpened, d->buildset, &ProjectBuildSetModel::loadFromProject ); connect( this, &ProjectController::projectClosing, d->buildset, &ProjectBuildSetModel::saveToProject ); connect( this, &ProjectController::projectClosed, d->buildset, &ProjectBuildSetModel::projectClosed ); d->m_changesModel = new ProjectChangesModel(this); loadSettings(false); d->dialog = new ProjectDialogProvider(d); QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/ProjectController"), this, QDBusConnection::ExportScriptableSlots ); KSharedConfigPtr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); const auto projects = group.readEntry( "Open Projects", QList() ); connect( Core::self()->selectionController(), &ISelectionController::selectionChanged, this, [this]() { Q_D(ProjectController); d->updateActionStates(); } ); connect(this, &ProjectController::projectOpened, this, [this]() { Q_D(ProjectController); d->updateActionStates(); }); connect(this, &ProjectController::projectClosing, this, [this]() { Q_D(ProjectController); d->updateActionStates(); }); QTimer::singleShot(0, this, [this, projects](){ openProjects(projects); emit initialized(); }); } void ProjectController::openProjects(const QList& projects) { for (const QUrl& url : projects) { openProject(url); } } void ProjectController::loadSettings( bool projectIsLoaded ) { Q_UNUSED(projectIsLoaded) } void ProjectController::saveSettings( bool projectIsLoaded ) { Q_UNUSED( projectIsLoaded ); } int ProjectController::projectCount() const { Q_D(const ProjectController); return d->m_projects.count(); } IProject* ProjectController::projectAt( int num ) const { Q_D(const ProjectController); if( !d->m_projects.isEmpty() && num >= 0 && num < d->m_projects.count() ) return d->m_projects.at( num ); return nullptr; } QList ProjectController::projects() const { Q_D(const ProjectController); return d->m_projects; } void ProjectController::eventuallyOpenProjectFile(KIO::Job* _job, const KIO::UDSEntryList& entries) { Q_D(ProjectController); auto* job = qobject_cast(_job); Q_ASSERT(job); for (const KIO::UDSEntry& entry : entries) { if(d->m_foundProjectFile) break; if(!entry.isDir()) { QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if(name.endsWith(QLatin1String(".kdev4"))) { //We have found a project-file, open it openProject(Path(Path(job->url()), name).toUrl()); d->m_foundProjectFile = true; } } } } void ProjectController::openProjectForUrlSlot(bool) { if(ICore::self()->documentController()->activeDocument()) { QUrl url = ICore::self()->documentController()->activeDocument()->url(); IProject* project = ICore::self()->projectController()->findProjectForUrl(url); if(!project) { openProjectForUrl(url); }else{ auto* message = new Sublime::Message(i18n("Project already open: %1", project->name()), Sublime::Message::Error); Core::self()->uiController()->postMessage(message); } }else{ auto* message = new Sublime::Message(i18n("No active document"), Sublime::Message::Error); Core::self()->uiController()->postMessage(message); } } void ProjectController::openProjectForUrl(const QUrl& sourceUrl) { Q_D(ProjectController); Q_ASSERT(!sourceUrl.isRelative()); QUrl dirUrl = sourceUrl; if (sourceUrl.isLocalFile() && !QFileInfo(sourceUrl.toLocalFile()).isDir()) { dirUrl = dirUrl.adjusted(QUrl::RemoveFilename); } QUrl testAt = dirUrl; d->m_foundProjectFile = false; while(!testAt.path().isEmpty()) { KIO::ListJob* job = KIO::listDir(testAt); connect(job, &KIO::ListJob::entries, this, &ProjectController::eventuallyOpenProjectFile); KJobWidgets::setWindow(job, ICore::self()->uiController()->activeMainWindow()); job->exec(); if(d->m_foundProjectFile) { //Fine! We have directly opened the project d->m_foundProjectFile = false; return; } QUrl oldTest = testAt.adjusted(QUrl::RemoveFilename); if(oldTest == testAt) break; } QUrl askForOpen = d->dialog->askProjectConfigLocation(false, dirUrl); if(askForOpen.isValid()) openProject(askForOpen); } void ProjectController::openProject( const QUrl &projectFile ) { Q_D(ProjectController); QUrl url = projectFile; if ( url.isEmpty() ) { url = d->dialog->askProjectConfigLocation(false); if ( url.isEmpty() ) { return; } } Q_ASSERT(!url.isRelative()); QList existingSessions; if(!Core::self()->sessionController()->activeSession()->containedProjects().contains(url)) { const auto sessions = Core::self()->sessionController()->sessions(); for (const Session* session : sessions) { if(session->containedProjects().contains(url)) { existingSessions << session; #if 0 ///@todo Think about this! Problem: The session might already contain files, the debugger might be active, etc. //If this session is empty, close it if(Core::self()->sessionController()->activeSession()->description().isEmpty()) { //Terminate this instance of kdevelop if the user agrees const auto windows = Core::self()->uiController()->controller()->mainWindows(); for (Sublime::MainWindow* window : windows) { window->close(); } } #endif } } } if ( ! existingSessions.isEmpty() ) { ScopedDialog dialog(Core::self()->uiControllerInternal()->activeMainWindow()); dialog->setWindowTitle(i18n("Project Already Open")); auto mainLayout = new QVBoxLayout(dialog); mainLayout->addWidget(new QLabel(i18n("The project you're trying to open is already open in at least one " "other session.
What do you want to do?"))); QGroupBox sessions; sessions.setLayout(new QVBoxLayout); QRadioButton* newSession = new QRadioButton(i18n("Add project to current session")); sessions.layout()->addWidget(newSession); newSession->setChecked(true); for (const Session* session : qAsConst(existingSessions)) { QRadioButton* button = new QRadioButton(i18n("Open session %1", session->description())); button->setProperty("sessionid", session->id().toString()); sessions.layout()->addWidget(button); } sessions.layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); mainLayout->addWidget(&sessions); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Abort); auto okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject); mainLayout->addWidget(buttonBox); if (!dialog->exec()) return; for (const QObject* obj : sessions.children()) { if ( const auto* button = qobject_cast(obj) ) { QString sessionid = button->property("sessionid").toString(); if ( button->isChecked() && ! sessionid.isEmpty() ) { Core::self()->sessionController()->loadSession(sessionid); return; } } } } if ( url.isEmpty() ) { url = d->dialog->askProjectConfigLocation(false); } if ( !url.isEmpty() ) { d->importProject(url); } } bool ProjectController::fetchProjectFromUrl(const QUrl& repoUrl, FetchFlags fetchFlags) { Q_D(ProjectController); IPlugin* vcsOrProviderPlugin = nullptr; // TODO: query also projectprovider plugins, and that before plain vcs plugins // e.g. KDE provider plugin could catch URLs from mirror or pickup kde:repo things auto* pluginController = d->m_core->pluginController(); const auto& vcsPlugins = pluginController->allPluginsForExtension(QStringLiteral("org.kdevelop.IBasicVersionControl")); for (auto* plugin : vcsPlugins) { auto* iface = plugin->extension(); if (iface->isValidRemoteRepositoryUrl(repoUrl)) { vcsOrProviderPlugin = plugin; break; } } if (!vcsOrProviderPlugin) { if (fetchFlags.testFlag(FetchShowErrorIfNotSupported)) { const QString messageText = i18n("No enabled plugin supports this repository URL: %1", repoUrl.toDisplayString()); auto* message = new Sublime::Message(messageText, Sublime::Message::Error); ICore::self()->uiController()->postMessage(message); } return false; } const QUrl url = d->dialog->askProjectConfigLocation(true, QUrl(), repoUrl, vcsOrProviderPlugin); if (!url.isEmpty()) { d->importProject(url); } return true; } void ProjectController::fetchProject() { Q_D(ProjectController); QUrl url = d->dialog->askProjectConfigLocation(true); if ( !url.isEmpty() ) { d->importProject(url); } } void ProjectController::projectImportingFinished( IProject* project ) { Q_D(ProjectController); if( !project ) { qCWarning(SHELL) << "OOOPS: 0-pointer project"; return; } IPlugin *managerPlugin = project->managerPlugin(); QList pluglist; pluglist.append( managerPlugin ); d->m_projectPlugins.insert( project, pluglist ); d->m_projects.append( project ); if ( d->m_currentlyOpening.isEmpty() ) { d->saveListOfOpenedProjects(); } if (Core::self()->setupFlags() != Core::NoUi) { d->m_recentProjectsAction->addUrl( project->projectFile().toUrl() ); saveRecentProjectsActionEntries(); } Q_ASSERT(d->m_currentlyOpening.contains(project->projectFile().toUrl())); d->m_currentlyOpening.removeAll(project->projectFile().toUrl()); emit projectOpened( project ); reparseProject(project); } // helper method for closeProject() void ProjectController::unloadUnusedProjectPlugins(IProject* proj) { Q_D(ProjectController); - QList pluginsForProj = d->m_projectPlugins.value( proj ); + const QList pluginsForProj = d->m_projectPlugins.value( proj ); d->m_projectPlugins.remove( proj ); QList otherProjectPlugins; for (const QList& _list : qAsConst(d->m_projectPlugins)) { otherProjectPlugins << _list; } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QSet pluginsForProjSet(pluginsForProj.begin(), pluginsForProj.end()); + QSet otherPrjPluginsSet(otherProjectPlugins.constBegin(), otherProjectPlugins.constEnd()); +#else QSet pluginsForProjSet = QSet::fromList( pluginsForProj ); QSet otherPrjPluginsSet = QSet::fromList( otherProjectPlugins ); +#endif // loaded - target = tobe unloaded. const QSet tobeRemoved = pluginsForProjSet.subtract( otherPrjPluginsSet ); for (IPlugin* _plugin : tobeRemoved) { KPluginMetaData _plugInfo = Core::self()->pluginController()->pluginInfo( _plugin ); if( _plugInfo.isValid() ) { QString _plugName = _plugInfo.pluginId(); qCDebug(SHELL) << "about to unloading :" << _plugName; Core::self()->pluginController()->unloadPlugin( _plugName ); } } } // helper method for closeProject() void ProjectController::closeAllOpenedFiles(IProject* proj) { const auto documents = Core::self()->documentController()->openDocuments(); for (IDocument* doc : documents) { if (proj->inProject(IndexedString(doc->url()))) { doc->close(); } } } // helper method for closeProject() void ProjectController::initializePluginCleanup(IProject* proj) { // Unloading (and thus deleting) these plugins is not a good idea just yet // as we're being called by the view part and it gets deleted when we unload the plugin(s) // TODO: find a better place to unload connect(proj, &IProject::destroyed, this, [this] { Q_D(ProjectController); d->unloadAllProjectPlugins(); }); } void ProjectController::takeProject(IProject* proj) { Q_D(ProjectController); if (!proj) { return; } // loading might have failed d->m_currentlyOpening.removeAll(proj->projectFile().toUrl()); d->m_projects.removeAll(proj); emit projectClosing(proj); //Core::self()->saveSettings(); // The project file is being closed. // Now we can save settings for all of the Core // objects including this one!! unloadUnusedProjectPlugins(proj); closeAllOpenedFiles(proj); proj->close(); if (d->m_projects.isEmpty()) { initializePluginCleanup(proj); } if(!d->m_cleaningUp) d->saveListOfOpenedProjects(); emit projectClosed(proj); } void ProjectController::closeProject(IProject* proj) { takeProject(proj); proj->deleteLater(); // be safe when deleting } void ProjectController::closeAllProjects() { Q_D(ProjectController); const auto projects = d->m_projects; for (auto* project : projects) { closeProject(project); } } void ProjectController::abortOpeningProject(IProject* proj) { Q_D(ProjectController); d->m_currentlyOpening.removeAll(proj->projectFile().toUrl()); emit projectOpeningAborted(proj); } ProjectModel* ProjectController::projectModel() { Q_D(ProjectController); return d->model; } IProject* ProjectController::findProjectForUrl( const QUrl& url ) const { Q_D(const ProjectController); if (d->m_projects.isEmpty()) { return nullptr; } ProjectBaseItem* item = d->model->itemForPath(IndexedString(url)); if (item) { return item->project(); } return nullptr; } IProject* ProjectController::findProjectByName( const QString& name ) { Q_D(ProjectController); auto it = std::find_if(d->m_projects.constBegin(), d->m_projects.constEnd(), [&](IProject* proj) { return (proj->name() == name); }); return (it != d->m_projects.constEnd()) ? *it : nullptr; } void ProjectController::configureProject( IProject* project ) { Q_D(ProjectController); d->projectConfig( project ); } void ProjectController::addProject(IProject* project) { Q_D(ProjectController); Q_ASSERT(project); if (d->m_projects.contains(project)) { qCWarning(SHELL) << "Project already tracked by this project controller:" << project; return; } // fake-emit signals so listeners are aware of a new project being added emit projectAboutToBeOpened(project); project->setParent(this); d->m_projects.append(project); emit projectOpened(project); } bool ProjectController::isProjectNameUsed( const QString& name ) const { const auto projects = this->projects(); return std::any_of(projects.begin(), projects.end(), [&](IProject* p) { return (p->name() == name); }); } QUrl ProjectController::projectsBaseDirectory() const { KConfigGroup group = ICore::self()->activeSession()->config()->group( "Project Manager" ); return group.readEntry("Projects Base Directory", QUrl::fromLocalFile(QDir::homePath() + QLatin1String("/projects"))); } QString ProjectController::prettyFilePath(const QUrl& url, FormattingOptions format) const { IProject* project = Core::self()->projectController()->findProjectForUrl(url); if(!project) { // Find a project with the correct base directory at least const auto projects = Core::self()->projectController()->projects(); auto it = std::find_if(projects.begin(), projects.end(), [&](IProject* candidateProject) { return (candidateProject->path().toUrl().isParentOf(url)); }); if (it != projects.end()) { project = *it; } } Path parent = Path(url).parent(); QString prefixText; if (project) { if (format == FormatHtml) { prefixText = QLatin1String("") + project->name() + QLatin1String("/"); } else { prefixText = project->name() + QLatin1Char(':'); } QString relativePath = project->path().relativePath(parent); if(relativePath.startsWith(QLatin1String("./"))) { relativePath.remove(0, 2); } if (!relativePath.isEmpty()) { prefixText += relativePath + QLatin1Char('/'); } } else { prefixText = parent.pathOrUrl() + QLatin1Char('/'); } return prefixText; } QString ProjectController::prettyFileName(const QUrl& url, FormattingOptions format) const { IProject* project = Core::self()->projectController()->findProjectForUrl(url); if(project && project->path() == Path(url)) { if (format == FormatHtml) { return QLatin1String("") + project->name() + QLatin1String(""); } else { return project->name(); } } QString prefixText = prettyFilePath( url, format ); if (format == FormatHtml) { return prefixText + QLatin1String("") + url.fileName() + QLatin1String(""); } else { return prefixText + url.fileName(); } } ContextMenuExtension ProjectController::contextMenuExtension(Context* ctx, QWidget* parent) { Q_D(ProjectController); Q_UNUSED(parent); ContextMenuExtension ext; if ( ctx->type() != Context::ProjectItemContext) { return ext; } if (!static_cast(ctx)->items().isEmpty() ) { auto* action = new QAction(i18n("Reparse the Entire Project"), this); connect(action, &QAction::triggered, this, [this] { Q_D(ProjectController); const auto projects = d->selectedProjects(); for (auto* project : projects) { reparseProject(project, true, true); } }); ext.addAction(ContextMenuExtension::ProjectGroup, action); return ext; } ext.addAction(ContextMenuExtension::ProjectGroup, d->m_openProject); ext.addAction(ContextMenuExtension::ProjectGroup, d->m_fetchProject); ext.addAction(ContextMenuExtension::ProjectGroup, d->m_recentProjectsAction); return ext; } ProjectBuildSetModel* ProjectController::buildSetModel() { Q_D(ProjectController); return d->buildset; } ProjectChangesModel* ProjectController::changesModel() { Q_D(ProjectController); return d->m_changesModel; } void ProjectController::commitCurrentProject() { IDocument* doc=ICore::self()->documentController()->activeDocument(); if(!doc) return; QUrl url=doc->url(); IProject* project = ICore::self()->projectController()->findProjectForUrl(url); if(project && project->versionControlPlugin()) { IPlugin* plugin = project->versionControlPlugin(); auto* vcs=plugin->extension(); if(vcs) { ICore::self()->documentController()->saveAllDocuments(KDevelop::IDocument::Silent); const Path basePath = project->path(); VCSCommitDiffPatchSource* patchSource = new VCSCommitDiffPatchSource(new VCSStandardDiffUpdater(vcs, basePath.toUrl())); bool ret = showVcsDiff(patchSource); if(!ret) { ScopedDialog commitDialog(patchSource); commitDialog->setCommitCandidates(patchSource->infos()); commitDialog->exec(); } } } } QString ProjectController::mapSourceBuild( const QString& path_, bool reverse, bool fallbackRoot ) const { Q_D(const ProjectController); Path path(path_); IProject* sourceDirProject = nullptr, *buildDirProject = nullptr; for (IProject* proj : qAsConst(d->m_projects)) { if(proj->path().isParentOf(path) || proj->path() == path) sourceDirProject = proj; if(proj->buildSystemManager()) { Path buildDir = proj->buildSystemManager()->buildDirectory(proj->projectItem()); if(buildDir.isValid() && (buildDir.isParentOf(path) || buildDir == path)) buildDirProject = proj; } } if(!reverse) { // Map-target is the build directory if(sourceDirProject && sourceDirProject->buildSystemManager()) { // We're in the source, map into the build directory QString relativePath = sourceDirProject->path().relativePath(path); Path build = sourceDirProject->buildSystemManager()->buildDirectory(sourceDirProject->projectItem()); build.addPath(relativePath); while(!QFile::exists(build.path())) build = build.parent(); return build.pathOrUrl(); }else if(buildDirProject && fallbackRoot) { // We're in the build directory, map to the build directory root return buildDirProject->buildSystemManager()->buildDirectory(buildDirProject->projectItem()).pathOrUrl(); } }else{ // Map-target is the source directory if(buildDirProject) { Path build = buildDirProject->buildSystemManager()->buildDirectory(buildDirProject->projectItem()); // We're in the source, map into the build directory QString relativePath = build.relativePath(path); Path source = buildDirProject->path(); source.addPath(relativePath); while(!QFile::exists(source.path())) source = source.parent(); return source.pathOrUrl(); }else if(sourceDirProject && fallbackRoot) { // We're in the source directory, map to the root return sourceDirProject->path().pathOrUrl(); } } return QString(); } void KDevelop::ProjectController::reparseProject(IProject *project, bool forceUpdate, bool forceAll) { Q_D(ProjectController); if (auto job = d->m_parseJobs.value(project)) { job->kill(); } d->m_parseJobs[project] = new KDevelop::ParseProjectJob(project, forceUpdate, forceAll); ICore::self()->runController()->registerJob(d->m_parseJobs[project]); } } diff --git a/kdevplatform/shell/workingsets/workingset.cpp b/kdevplatform/shell/workingsets/workingset.cpp index db736e3034..b5b00b2924 100644 --- a/kdevplatform/shell/workingsets/workingset.cpp +++ b/kdevplatform/shell/workingsets/workingset.cpp @@ -1,585 +1,595 @@ /* Copyright David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "workingset.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #define SYNC_OFTEN using namespace KDevelop; bool WorkingSet::m_loading = false; namespace { QIcon generateIcon(const WorkingSetIconParameters& params) { QImage pixmap(16, 16, QImage::Format_ARGB32); // fill the background with a transparent color pixmap.fill(QColor::fromRgba(qRgba(0, 0, 0, 0))); const uint coloredCount = params.coloredCount; // coordinates of the rectangles to draw, for 16x16 icons specifically QList rects{ {1, 1, 5, 5}, {1, 9, 5, 5}, {9, 1, 5, 5}, {9, 9, 5, 5}, }; if ( params.swapDiagonal ) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) rects.swapItemsAt(1, 2); #else rects.swap(1, 2); #endif } QPainter painter(&pixmap); // color for non-colored squares, paint them brighter if the working set is the active one const int inact = 40; QColor darkColor = QColor::fromRgb(inact, inact, inact); // color for colored squares // this code is not fragile, you can just tune the magic formulas at random and see what looks good. // just make sure to keep it within the 0-360 / 0-255 / 0-255 space of the HSV model QColor brightColor = QColor::fromHsv(params.hue, qMin(255, 215 + (params.setId*5) % 150), 205 + (params.setId*11) % 50); // Y'UV "Y" value, the approximate "lightness" of the color // If it is above 0.6, then making the color darker a bit is okay, // if it is below 0.35, then the color should be a bit brighter. float brightY = 0.299 * brightColor.redF() + 0.587 * brightColor.greenF() + 0.114 * brightColor.blueF(); if ( brightY > 0.6 ) { if ( params.setId % 7 < 2 ) { // 2/7 chance to make the color significantly darker brightColor = brightColor.darker(120 + (params.setId*7) % 35); } else if ( params.setId % 5 == 0 ) { // 1/5 chance to make it a bit darker brightColor = brightColor.darker(110 + (params.setId*3) % 10); } } if ( brightY < 0.35 ) { // always make the color brighter to avoid very dark colors (like rgb(0, 0, 255)) brightColor = brightColor.lighter(120 + (params.setId*13) % 55); } int at = 0; for (const QRect& rect : qAsConst(rects)) { QColor currentColor; // pick the colored squares; you can get different patterns by re-ordering the "rects" list if ( (at + params.setId*7) % 4 < coloredCount ) { currentColor = brightColor; } else { currentColor = darkColor; } // draw the filling of the square painter.setPen(QColor(currentColor)); painter.setBrush(QBrush(currentColor)); painter.drawRect(rect); // draw a slight set-in shadow for the square -- it's barely recognizeable, // but it looks way better than without painter.setBrush(Qt::NoBrush); painter.setPen(QColor(0, 0, 0, 50)); painter.drawRect(rect); painter.setPen(QColor(0, 0, 0, 25)); painter.drawRect(rect.x() + 1, rect.y() + 1, rect.width() - 2, rect.height() - 2); at += 1; } return QIcon(QPixmap::fromImage(pixmap)); } } WorkingSet::WorkingSet(const QString& id) : QObject() , m_id(id) , m_icon(generateIcon(WorkingSetIconParameters(id))) { } void WorkingSet::saveFromArea( Sublime::Area* a, Sublime::AreaIndex * area, KConfigGroup setGroup, KConfigGroup areaGroup ) { if (area->isSplit()) { setGroup.writeEntry("Orientation", area->orientation() == Qt::Horizontal ? "Horizontal" : "Vertical"); if (area->first()) { saveFromArea(a, area->first(), KConfigGroup(&setGroup, "0"), KConfigGroup(&areaGroup, "0")); } if (area->second()) { saveFromArea(a, area->second(), KConfigGroup(&setGroup, "1"), KConfigGroup(&areaGroup, "1")); } } else { setGroup.writeEntry("View Count", area->viewCount()); areaGroup.writeEntry("View Count", area->viewCount()); int index = 0; const auto views = area->views(); for (Sublime::View* view : views) { //The working set config gets an updated list of files QString docSpec = view->document()->documentSpecifier(); //only save the documents from protocols KIO understands //otherwise we try to load kdev:// too early if (!KProtocolInfo::isKnownProtocol(QUrl(docSpec))) { continue; } setGroup.writeEntry(QStringLiteral("View %1").arg(index), docSpec); //The area specific config stores the working set documents in order along with their state areaGroup.writeEntry(QStringLiteral("View %1").arg(index), docSpec); KConfigGroup viewGroup(&areaGroup, QStringLiteral("View %1 Config").arg(index)); view->writeSessionConfig(viewGroup); ++index; } } } bool WorkingSet::isEmpty() const { KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup group = setConfig.group(m_id); return !group.hasKey("Orientation") && group.readEntry("View Count", 0) == 0; } struct DisableMainWindowUpdatesFromArea { explicit DisableMainWindowUpdatesFromArea(Sublime::Area* area) : m_area(area) { if(area) { const auto windows = Core::self()->uiControllerInternal()->mainWindows(); for (Sublime::MainWindow* window : windows) { if(window->area() == area) { if(window->updatesEnabled()) { wasUpdatesEnabled.insert(window); window->setUpdatesEnabled(false); } } } } } ~DisableMainWindowUpdatesFromArea() { if(m_area) { for (Sublime::MainWindow* window : qAsConst(wasUpdatesEnabled)) { window->setUpdatesEnabled(wasUpdatesEnabled.contains(window)); } } } private: Q_DISABLE_COPY(DisableMainWindowUpdatesFromArea) Sublime::Area* m_area; QSet wasUpdatesEnabled; }; void loadFileList(QStringList& ret, const KConfigGroup& group) { if (group.hasKey("Orientation")) { QStringList subgroups = group.groupList(); if (subgroups.contains(QStringLiteral("0"))) { { KConfigGroup subgroup(&group, "0"); loadFileList(ret, subgroup); } if (subgroups.contains(QStringLiteral("1"))) { KConfigGroup subgroup(&group, "1"); loadFileList(ret, subgroup); } } } else { int viewCount = group.readEntry("View Count", 0); ret.reserve(ret.size() + viewCount); for (int i = 0; i < viewCount; ++i) { QString specifier = group.readEntry(QStringLiteral("View %1").arg(i), QString()); ret << specifier; } } } QStringList WorkingSet::fileList() const { QStringList ret; KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup group = setConfig.group(m_id); loadFileList(ret, group); return ret; } +QSet WorkingSet::fileSet() const +{ + const QStringList fileList = this->fileList(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + return QSet(fileList.begin(), fileList.end()); +#else + return fileList.toSet(); +#endif +} + void WorkingSet::loadToArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex) { PushValue enableLoading(m_loading, true); /// We cannot disable the updates here, because (probably) due to a bug in Qt, /// which causes the updates to stay disabled forever after some complex operations /// on the sub-views. This could be reproduced by creating two working-sets with complex /// split-view configurations and switching between them. Re-enabling the updates doesn't help. // DisableMainWindowUpdatesFromArea updatesDisabler(area); qCDebug(SHELL) << "loading working-set" << m_id << "into area" << area; QMultiMap recycle; const auto viewsBefore = area->views(); for (Sublime::View* view : viewsBefore) { recycle.insert( view->document()->documentSpecifier(), area->removeView(view) ); } qCDebug(SHELL) << "recycling" << recycle.size() << "old views"; Q_ASSERT( area->views().empty() ); KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup setGroup = setConfig.group(m_id); KConfigGroup areaGroup = setConfig.group(m_id + QLatin1Char('|') + area->title()); loadToArea(area, areaIndex, setGroup, areaGroup, recycle); // Delete views which were not recycled qCDebug(SHELL) << "deleting " << recycle.size() << " old views"; qDeleteAll( recycle ); area->setActiveView(nullptr); //activate view in the working set /// @todo correctly select one out of multiple equal views QString activeView = areaGroup.readEntry("Active View", QString()); const auto viewsAfter = area->views(); for (Sublime::View* v : viewsAfter) { if (v->document()->documentSpecifier() == activeView) { area->setActiveView(v); break; } } if( !area->activeView() && !area->views().isEmpty() ) area->setActiveView( area->views().at(0) ); if( area->activeView() ) { const auto windows = Core::self()->uiControllerInternal()->mainWindows(); for (Sublime::MainWindow* window : windows) { if(window->area() == area) { window->activateView( area->activeView() ); } } } } void WorkingSet::loadToArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex, const KConfigGroup& setGroup, const KConfigGroup& areaGroup, QMultiMap& recycle) { Q_ASSERT( !areaIndex->isSplit() ); if (setGroup.hasKey("Orientation")) { QStringList subgroups = setGroup.groupList(); /// @todo also save and restore the ratio if (subgroups.contains(QStringLiteral("0")) && subgroups.contains(QStringLiteral("1"))) { // qCDebug(SHELL) << "has zero, split:" << split; Qt::Orientation orientation = setGroup.readEntry("Orientation", "Horizontal") == QLatin1String("Vertical") ? Qt::Vertical : Qt::Horizontal; if(!areaIndex->isSplit()){ areaIndex->split(orientation); }else{ areaIndex->setOrientation(orientation); } loadToArea(area, areaIndex->first(), KConfigGroup(&setGroup, "0"), KConfigGroup(&areaGroup, "0"), recycle); loadToArea(area, areaIndex->second(), KConfigGroup(&setGroup, "1"), KConfigGroup(&areaGroup, "1"), recycle); if( areaIndex->first()->viewCount() == 0 ) areaIndex->unsplit(areaIndex->first()); else if( areaIndex->second()->viewCount() == 0 ) areaIndex->unsplit(areaIndex->second()); } } else { //Load all documents from the workingset into this areaIndex int viewCount = setGroup.readEntry("View Count", 0); QMap createdViews; for (int i = 0; i < viewCount; ++i) { QString specifier = setGroup.readEntry(QStringLiteral("View %1").arg(i), QString()); if (specifier.isEmpty()) { continue; } Sublime::View* previousView = area->views().empty() ? nullptr : area->views().at(area->views().size() - 1); QMultiMap::iterator it = recycle.find( specifier ); if( it != recycle.end() ) { area->addView( *it, areaIndex, previousView ); recycle.erase( it ); continue; } IDocument* doc = Core::self()->documentControllerInternal()->openDocument(QUrl::fromUserInput(specifier), KTextEditor::Cursor::invalid(), IDocumentController::DoNotActivate | IDocumentController::DoNotCreateView); auto *document = dynamic_cast(doc); if (document) { Sublime::View* view = document->createView(); area->addView(view, areaIndex, previousView); createdViews[i] = view; } else { qCWarning(SHELL) << "Unable to create view" << specifier; } } //Load state for (int i = 0; i < viewCount; ++i) { KConfigGroup viewGroup(&areaGroup, QStringLiteral("View %1 Config").arg(i)); if (viewGroup.exists() && createdViews.contains(i)) createdViews[i]->readSessionConfig(viewGroup); } } } void deleteGroupRecursive(KConfigGroup group) { // qCDebug(SHELL) << "deleting" << group.name(); const auto entryMap = group.entryMap(); for (auto it = entryMap.begin(), end = entryMap.end(); it != end; ++it) { group.deleteEntry(it.key()); } Q_ASSERT(group.entryMap().isEmpty()); const auto groupList = group.groupList(); for (const QString& subGroup : groupList) { deleteGroupRecursive(group.group(subGroup)); group.deleteGroup(subGroup); } //Why doesn't this work? // Q_ASSERT(group.groupList().isEmpty()); group.deleteGroup(); } void WorkingSet::deleteSet(bool force, bool silent) { if(m_areas.isEmpty() || force) { emit aboutToRemove(this); KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup group = setConfig.group(m_id); deleteGroupRecursive(group); #ifdef SYNC_OFTEN setConfig.sync(); #endif if(!silent) emit setChangedSignificantly(); } } void WorkingSet::saveFromArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex) { qCDebug(SHELL) << "saving" << m_id << "from area"; bool wasPersistent = isPersistent(); KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup setGroup = setConfig.group(m_id); deleteGroupRecursive(setGroup); KConfigGroup areaGroup = setConfig.group(m_id + QLatin1Char('|') + area->title()); QString lastActiveView = areaGroup.readEntry("Active View", ""); deleteGroupRecursive(areaGroup); if (area->activeView() && area->activeView()->document()) areaGroup.writeEntry("Active View", area->activeView()->document()->documentSpecifier()); else areaGroup.writeEntry("Active View", lastActiveView); saveFromArea(area, areaIndex, setGroup, areaGroup); if(isEmpty()) { deleteGroupRecursive(setGroup); deleteGroupRecursive(areaGroup); } setPersistent(wasPersistent); #ifdef SYNC_OFTEN setConfig.sync(); #endif emit setChangedSignificantly(); } void WorkingSet::areaViewAdded(Sublime::AreaIndex*, Sublime::View*) { auto* area = qobject_cast(sender()); Q_ASSERT(area); Q_ASSERT(area->workingSet() == m_id); qCDebug(SHELL) << "added view in" << area << ", id" << m_id; if (m_loading) { qCDebug(SHELL) << "doing nothing because loading"; return; } changed(area); } void WorkingSet::areaViewRemoved(Sublime::AreaIndex*, Sublime::View* view) { auto* area = qobject_cast(sender()); Q_ASSERT(area); Q_ASSERT(area->workingSet() == m_id); qCDebug(SHELL) << "removed view in" << area << ", id" << m_id; if (m_loading) { qCDebug(SHELL) << "doing nothing because loading"; return; } const auto areasBefore = m_areas; // TODO: check if areas could be changed, otherwise use m_areas directly for (Sublime::Area* otherArea : areasBefore) { if(otherArea == area) continue; const auto otherAreaViews = otherArea->views(); bool hadDocument = std::any_of(otherAreaViews.begin(), otherAreaViews.end(), [&](Sublime::View* otherView) { return (view->document() == otherView->document()); }); if(!hadDocument) { // We do this to prevent UI flicker. The view has already been removed from // one of the connected areas, so the working-set has already recorded the change. return; } } changed(area); } void WorkingSet::setPersistent(bool persistent) { KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup group = setConfig.group(m_id); group.writeEntry("persistent", persistent); #ifdef SYNC_OFTEN group.sync(); #endif qCDebug(SHELL) << "setting" << m_id << "persistent:" << persistent; } bool WorkingSet::isPersistent() const { KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets"); KConfigGroup group = setConfig.group(m_id); return group.readEntry("persistent", false); } QIcon WorkingSet::icon() const { return m_icon; } bool WorkingSet::isConnected( Sublime::Area* area ) { return m_areas.contains( area ); } QString WorkingSet::id() const { return m_id; } bool WorkingSet::hasConnectedAreas() const { return !m_areas.isEmpty(); } bool WorkingSet::hasConnectedAreas(const QList& areas) const { for (Sublime::Area* area : areas) { if (m_areas.contains(area)) return true; } return false; } void WorkingSet::connectArea( Sublime::Area* area ) { if ( m_areas.contains( area ) ) { qCDebug(SHELL) << "tried to double-connect area"; return; } qCDebug(SHELL) << "connecting" << m_id << "to area" << area; // Q_ASSERT(area->workingSet() == m_id); m_areas.push_back( area ); connect( area, &Sublime::Area::viewAdded, this, &WorkingSet::areaViewAdded ); connect( area, &Sublime::Area::viewRemoved, this, &WorkingSet::areaViewRemoved ); } void WorkingSet::disconnectArea( Sublime::Area* area ) { if ( !m_areas.contains( area ) ) { qCDebug(SHELL) << "tried to disconnect not connected area"; return; } qCDebug(SHELL) << "disconnecting" << m_id << "from area" << area; // Q_ASSERT(area->workingSet() == m_id); disconnect( area, &Sublime::Area::viewAdded, this, &WorkingSet::areaViewAdded ); disconnect( area, &Sublime::Area::viewRemoved, this, &WorkingSet::areaViewRemoved ); m_areas.removeAll( area ); } void WorkingSet::changed( Sublime::Area* area ) { if ( m_loading ) { return; } { //Do not capture changes done while loading PushValue enableLoading( m_loading, true ); qCDebug(SHELL) << "recording change done to" << m_id; saveFromArea( area, area->rootIndex() ); for (auto& a : qAsConst(m_areas)) { if (a != area ) { loadToArea(a, a->rootIndex()); } } } emit setChangedSignificantly(); } diff --git a/kdevplatform/shell/workingsets/workingset.h b/kdevplatform/shell/workingsets/workingset.h index ea280ea19e..517e3d56cc 100644 --- a/kdevplatform/shell/workingsets/workingset.h +++ b/kdevplatform/shell/workingsets/workingset.h @@ -1,128 +1,131 @@ /* Copyright David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef KDEVPLATFORM_WORKINGSET_H #define KDEVPLATFORM_WORKINGSET_H #include #include #include #include +#include namespace Sublime { class Area; class AreaIndex; class View; } namespace KDevelop { /// Contains all significant parameters which control the appearance of a working set icon struct WorkingSetIconParameters { explicit WorkingSetIconParameters(const QString& id) : setId(qHash(id) % 268435459) , coloredCount((setId % 15 < 4) ? 1 : (setId % 15 < 10) ? 2 : (setId % 15 == 14) ? 4 : 3) , hue((setId % 273 * 83) % 360) , swapDiagonal(setId % 31 < 16) { }; // calculate layout and colors depending on the working set ID // modulo it so it's around 2^28, leaving some space before uint overflows const uint setId; // amount of colored squares in this icon (the rest is grey or whatever you set as default color) // use 4-6-4-1 weighting for 1, 2, 3, 4 squares, because that's the number of arrangements for each const uint coloredCount; const uint hue; bool swapDiagonal; // between 0 and 100, 100 = very similar, 0 = very different // 20 points should make a significantly different icon. uint similarity(const WorkingSetIconParameters& other) const { int sim = 100; uint hueDiff = qAbs(hue - other.hue); hueDiff = hueDiff > 180 ? 360 - hueDiff : hueDiff; sim -= hueDiff > 35 ? 50 : (hueDiff * 50) / 180; if ( coloredCount != other.coloredCount ) { sim -= 50; } else if ( coloredCount == 2 && swapDiagonal != other.swapDiagonal ) { sim -= 35; } return sim; }; }; class WorkingSet : public QObject { Q_OBJECT public: explicit WorkingSet(const QString& id); bool isConnected(Sublime::Area* area); QIcon icon() const; bool isPersistent() const; void setPersistent(bool persistent); QString id() const; QStringList fileList() const; + QSet fileSet() const; + bool isEmpty() const; ///Updates this working-set from the given area and area-index void saveFromArea(Sublime::Area* area, Sublime::AreaIndex * areaIndex); ///Loads this working-set directly from the configuration file, and stores it in the given area ///Does not ask the user, this should be done beforehand. void loadToArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex); bool hasConnectedAreas() const; bool hasConnectedAreas(const QList& areas) const; void connectArea(Sublime::Area* area); void disconnectArea(Sublime::Area* area); void deleteSet(bool force, bool silent = false); private Q_SLOTS: void areaViewAdded(Sublime::AreaIndex* /*index*/, Sublime::View* /*view*/); void areaViewRemoved(Sublime::AreaIndex* /*index*/, Sublime::View* /*view*/); Q_SIGNALS: void setChangedSignificantly(); void aboutToRemove(WorkingSet*); private: void changed(Sublime::Area* area); void saveFromArea(Sublime::Area* area, Sublime::AreaIndex *areaIndex, KConfigGroup setGroup, KConfigGroup areaGroup); void loadToArea(Sublime::Area* area, Sublime::AreaIndex *areaIndex, const KConfigGroup& setGroup, const KConfigGroup& areaGroup, QMultiMap& recycle); const QString m_id; const QIcon m_icon; QVector> m_areas; static bool m_loading; }; } #endif // KDEVPLATFORM_WORKINGSET_H diff --git a/kdevplatform/shell/workingsets/workingsettoolbutton.cpp b/kdevplatform/shell/workingsets/workingsettoolbutton.cpp index 74bb3d2835..8106672835 100644 --- a/kdevplatform/shell/workingsets/workingsettoolbutton.cpp +++ b/kdevplatform/shell/workingsets/workingsettoolbutton.cpp @@ -1,175 +1,175 @@ /* Copyright David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "workingsettoolbutton.h" #include #include #include #include "core.h" #include "mainwindow.h" #include "workingset.h" #include "workingsetcontroller.h" #include "workingsethelpers.h" #include "documentcontroller.h" #include #include using namespace KDevelop; WorkingSetToolButton::WorkingSetToolButton(QWidget* parent, WorkingSet* set) : QToolButton(parent), m_set(set), m_toolTipEnabled(true) { setFocusPolicy(Qt::NoFocus); setWorkingSet(set); setAutoRaise(true); connect(this, &WorkingSetToolButton::clicked, this, &WorkingSetToolButton::buttonTriggered); } WorkingSet* WorkingSetToolButton::workingSet() const { return m_set; } void WorkingSetToolButton::setWorkingSet(WorkingSet* set) { m_set = set; setIcon(set ? set->icon() : QIcon()); } void WorkingSetToolButton::contextMenuEvent(QContextMenuEvent* ev) { showTooltip(ev->globalPos()); ev->accept(); } void WorkingSetToolButton::intersectSet() { Q_ASSERT(m_set); m_set->setPersistent(true); - filterViews(Core::self()->workingSetControllerInternal()->workingSet(mainWindow()->area()->workingSet())->fileList().toSet() & m_set->fileList().toSet()); + filterViews(Core::self()->workingSetControllerInternal()->workingSet(mainWindow()->area()->workingSet())->fileSet() & m_set->fileSet()); } void WorkingSetToolButton::subtractSet() { Q_ASSERT(m_set); m_set->setPersistent(true); - filterViews(Core::self()->workingSetControllerInternal()->workingSet(mainWindow()->area()->workingSet())->fileList().toSet() - m_set->fileList().toSet()); + filterViews(Core::self()->workingSetControllerInternal()->workingSet(mainWindow()->area()->workingSet())->fileSet() - m_set->fileSet()); } void WorkingSetToolButton::mergeSet() { Q_ASSERT(m_set); - const QSet loadFiles = m_set->fileList().toSet() - Core::self()->workingSetControllerInternal()->workingSet(mainWindow()->area()->workingSet())->fileList().toSet(); + const QSet loadFiles = m_set->fileSet() - Core::self()->workingSetControllerInternal()->workingSet(mainWindow()->area()->workingSet())->fileSet(); for (const QString& file : loadFiles) { Core::self()->documentController()->openDocument(QUrl::fromUserInput(file)); } } void WorkingSetToolButton::duplicateSet() { Q_ASSERT(m_set); if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default, true)) return; WorkingSet* set = Core::self()->workingSetControllerInternal()->newWorkingSet(QStringLiteral("clone")); set->setPersistent(true); set->saveFromArea(mainWindow()->area(), mainWindow()->area()->rootIndex()); mainWindow()->area()->setWorkingSet(set->id()); } void WorkingSetToolButton::loadSet() { Q_ASSERT(m_set); m_set->setPersistent(true); if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default, true)) return; mainWindow()->area()->setWorkingSet(QString(m_set->id())); } void WorkingSetToolButton::closeSet(bool ask) { Q_ASSERT(m_set); m_set->setPersistent(true); m_set->saveFromArea(mainWindow()->area(), mainWindow()->area()->rootIndex()); if(ask && !Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default, true)) return; mainWindow()->area()->setWorkingSet(QString()); } bool WorkingSetToolButton::event(QEvent* e) { if(m_toolTipEnabled && e->type() == QEvent::ToolTip) { auto* helpEvent = static_cast(e); showTooltip(helpEvent->globalPos()); e->accept(); return true; } return QToolButton::event(e); } void WorkingSetToolButton::showTooltip(const QPoint& globalPos) { Q_ASSERT(m_set); static WorkingSetToolButton* oldTooltipButton; WorkingSetController* controller = Core::self()->workingSetControllerInternal(); if(controller->tooltip() && oldTooltipButton == this) return; oldTooltipButton = this; controller->showToolTip(m_set, globalPos + QPoint(10, 20)); QRect extended(parentWidget()->mapToGlobal(geometry().topLeft()), parentWidget()->mapToGlobal(geometry().bottomRight())); controller->tooltip()->setHandleRect(extended); } void WorkingSetToolButton::buttonTriggered() { Q_ASSERT(m_set); if(mainWindow()->area()->workingSet() == m_set->id()) { showTooltip(QCursor::pos()); }else{ //Only close the working-set if the file was saved before if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default, true)) return; m_set->setPersistent(true); mainWindow()->area()->setWorkingSet(m_set->id()); } } diff --git a/kdevplatform/shell/workingsets/workingsettooltipwidget.cpp b/kdevplatform/shell/workingsets/workingsettooltipwidget.cpp index 8165c09c2c..ae7eb9376c 100644 --- a/kdevplatform/shell/workingsets/workingsettooltipwidget.cpp +++ b/kdevplatform/shell/workingsets/workingsettooltipwidget.cpp @@ -1,393 +1,393 @@ /* Copyright David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "workingsettooltipwidget.h" #include #include #include #include "debug.h" #include "core.h" #include "documentcontroller.h" #include "mainwindow.h" #include #include #include #include #include #include #include #include "workingset.h" #include "workingsetcontroller.h" #include "workingsetfilelabel.h" #include "workingsettoolbutton.h" #include "workingsethelpers.h" using namespace KDevelop; class FileWidget : public QWidget { Q_OBJECT public: QToolButton* m_button; class WorkingSetFileLabel* m_label; }; WorkingSetToolTipWidget::WorkingSetToolTipWidget(QWidget* parent, WorkingSet* set, MainWindow* mainwindow) : QWidget(parent), m_set(set) { auto* layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setMargin(0); connect(mainwindow->area(), &Sublime::Area::viewAdded, this, &WorkingSetToolTipWidget::updateFileButtons, Qt::QueuedConnection); connect(mainwindow->area(), &Sublime::Area::viewRemoved, this, &WorkingSetToolTipWidget::updateFileButtons, Qt::QueuedConnection); connect(Core::self()->workingSetControllerInternal(), &WorkingSetController::workingSetSwitched, this, &WorkingSetToolTipWidget::updateFileButtons); // title bar { auto* topLayout = new QHBoxLayout; m_setButton = new WorkingSetToolButton(this, set); m_setButton->hide(); topLayout->addSpacing(5); QLabel* icon = new QLabel; topLayout->addWidget(icon); topLayout->addSpacing(5); QString label; if (m_set->isConnected(mainwindow->area())) { label = i18n("Active Working Set"); } else { label = i18n("Working Set"); } QLabel* name = new QLabel(label); name->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); topLayout->addWidget(name); topLayout->addSpacing(10); icon->setPixmap(m_setButton->icon().pixmap(name->sizeHint().height()+8, name->sizeHint().height()+8)); topLayout->addStretch(); m_openButton = new QPushButton; m_openButton->setFlat(true); topLayout->addWidget(m_openButton); m_deleteButton = new QPushButton; m_deleteButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); m_deleteButton->setText(i18n("Delete")); m_deleteButton->setToolTip(i18n("Remove this working set. The contained documents are not affected.")); m_deleteButton->setFlat(true); connect(m_deleteButton, &QPushButton::clicked, m_set, [&] { m_set->deleteSet(false); }); connect(m_deleteButton, &QPushButton::clicked, this, &WorkingSetToolTipWidget::shouldClose); topLayout->addWidget(m_deleteButton); layout->addLayout(topLayout); // horizontal line QFrame* line = new QFrame(); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Raised); layout->addWidget(line); } // everything else is added to the following widget which just has a different background color auto* bodyLayout = new QVBoxLayout; { QWidget* body = new QWidget(); body->setLayout(bodyLayout); layout->addWidget(body); body->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); } // document list actions { auto* actionsLayout = new QHBoxLayout; m_documentsLabel = new QLabel(i18n("Documents:")); m_documentsLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); actionsLayout->addWidget(m_documentsLabel); actionsLayout->addStretch(); m_mergeButton = new QPushButton; m_mergeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); m_mergeButton->setText(i18n("Add All")); m_mergeButton->setToolTip(i18n("Add all documents that are part of this working set to the currently active working set.")); m_mergeButton->setFlat(true); connect(m_mergeButton, &QPushButton::clicked, m_setButton, &WorkingSetToolButton::mergeSet); actionsLayout->addWidget(m_mergeButton); m_subtractButton = new QPushButton; m_subtractButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); m_subtractButton->setText(i18n("Remove All")); m_subtractButton->setToolTip(i18n("Remove all documents that are part of this working set from the currently active working set.")); m_subtractButton->setFlat(true); connect(m_subtractButton, &QPushButton::clicked, m_setButton, &WorkingSetToolButton::subtractSet); actionsLayout->addWidget(m_subtractButton); bodyLayout->addLayout(actionsLayout); } QSet hadFiles; auto* filesLayout = new QVBoxLayout; filesLayout->setMargin(0); const auto setFiles = m_set->fileList(); for (const QString& file : setFiles) { if(hadFiles.contains(file)) continue; hadFiles.insert(file); auto* widget = new FileWidget; widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); auto* fileLayout = new QHBoxLayout(widget); auto* plusButton = new QToolButton; plusButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum); fileLayout->addWidget(plusButton); auto* fileLabel = new WorkingSetFileLabel; fileLabel->setTextFormat(Qt::RichText); // We add spaces behind and after, to make it look nicer fileLabel->setText(QLatin1String(" ") + Core::self()->projectController()->prettyFileName(QUrl::fromUserInput(file)) + QLatin1String(" ")); fileLabel->setToolTip(i18nc("@info:tooltip", "Click to open and activate this document.")); fileLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); fileLayout->addWidget(fileLabel); fileLayout->setMargin(0); plusButton->setMaximumHeight(fileLabel->sizeHint().height() + 4); plusButton->setMaximumWidth(plusButton->maximumHeight()); plusButton->setObjectName(file); fileLabel->setObjectName(file); fileLabel->setCursor(QCursor(Qt::PointingHandCursor)); widget->m_button = plusButton; widget->m_label = fileLabel; filesLayout->addWidget(widget); m_fileWidgets.insert(file, widget); m_orderedFileWidgets.push_back(widget); connect(plusButton, &QToolButton::clicked, this, &WorkingSetToolTipWidget::buttonClicked); connect(fileLabel, &WorkingSetFileLabel::clicked, this, &WorkingSetToolTipWidget::labelClicked); } bodyLayout->addLayout(filesLayout); updateFileButtons(); connect(set, &WorkingSet::setChangedSignificantly, this, &WorkingSetToolTipWidget::updateFileButtons); connect(mainwindow->area(), &Sublime::Area::changedWorkingSet, this, &WorkingSetToolTipWidget::updateFileButtons, Qt::QueuedConnection); QMetaObject::invokeMethod(this, "updateFileButtons"); } void WorkingSetToolTipWidget::nextDocument() { int active = -1; for(int a = 0; a < m_orderedFileWidgets.size(); ++a) if(m_orderedFileWidgets[a]->m_label->isActive()) active = a; if(active == -1) { qCWarning(SHELL) << "Found no active document"; return; } int next = (active + 1) % m_orderedFileWidgets.size(); while(m_orderedFileWidgets[next]->isHidden() && next != active) next = (next + 1) % m_orderedFileWidgets.size(); m_orderedFileWidgets[next]->m_label->emitClicked(); } void WorkingSetToolTipWidget::previousDocument() { int active = -1; for(int a = 0; a < m_orderedFileWidgets.size(); ++a) if(m_orderedFileWidgets[a]->m_label->isActive()) active = a; if(active == -1) { qCWarning(SHELL) << "Found no active document"; return; } int next = active - 1; if(next < 0) next += m_orderedFileWidgets.size(); while(m_orderedFileWidgets[next]->isHidden() && next != active) { next -= 1; if(next < 0) next += m_orderedFileWidgets.size(); } m_orderedFileWidgets[next]->m_label->emitClicked(); } void WorkingSetToolTipWidget::updateFileButtons() { auto* mainWindow = qobject_cast(Core::self()->uiController()->activeMainWindow()); Q_ASSERT(mainWindow); WorkingSetController* controller = Core::self()->workingSetControllerInternal(); ActiveToolTip* tooltip = controller->tooltip(); QString activeFile; if(mainWindow->area()->activeView()) activeFile = mainWindow->area()->activeView()->document()->documentSpecifier(); WorkingSet* currentWorkingSet = nullptr; QSet openFiles; if(!mainWindow->area()->workingSet().isEmpty()) { currentWorkingSet = controller->workingSet(mainWindow->area()->workingSet()); - openFiles = currentWorkingSet->fileList().toSet(); + openFiles = currentWorkingSet->fileSet(); } bool allOpen = true; bool noneOpen = true; bool needResize = false; bool allHidden = true; for(QMap< QString, FileWidget* >::iterator it = m_fileWidgets.begin(); it != m_fileWidgets.end(); ++it) { if(openFiles.contains(it.key())) { noneOpen = false; (*it)->m_button->setToolTip(i18n("Remove this file from the current working set")); (*it)->m_button->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); (*it)->show(); }else{ allOpen = false; (*it)->m_button->setToolTip(i18n("Add this file to the current working set")); (*it)->m_button->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); if(currentWorkingSet == m_set) { (*it)->hide(); needResize = true; } } if(!(*it)->isHidden()) allHidden = false; (*it)->m_label->setIsActiveFile(it.key() == activeFile); } // NOTE: always hide merge&subtract all on current working set // if we want to enable mergeButton, we have to fix it's behavior since it operates directly on the // set contents and not on the m_fileWidgets m_mergeButton->setHidden(allOpen || currentWorkingSet == m_set); m_subtractButton->setHidden(noneOpen || currentWorkingSet == m_set); m_deleteButton->setHidden(m_set->hasConnectedAreas()); m_documentsLabel->setHidden(m_mergeButton->isHidden() && m_subtractButton->isHidden() && m_deleteButton->isHidden()); if(currentWorkingSet == m_set) { disconnect(m_openButton, &QPushButton::clicked, m_setButton, &WorkingSetToolButton::loadSet); connect(m_openButton, &QPushButton::clicked, m_setButton, &WorkingSetToolButton::closeSet); connect(m_openButton, &QPushButton::clicked, this, &WorkingSetToolTipWidget::shouldClose); m_openButton->setIcon(QIcon::fromTheme(QStringLiteral("project-development-close"))); m_openButton->setText(i18n("Stash")); }else{ disconnect(m_openButton, &QPushButton::clicked, m_setButton, &WorkingSetToolButton::closeSet); connect(m_openButton, &QPushButton::clicked, m_setButton, &WorkingSetToolButton::loadSet); disconnect(m_openButton, &QPushButton::clicked, this, &WorkingSetToolTipWidget::shouldClose); m_openButton->setIcon(QIcon::fromTheme(QStringLiteral("project-open"))); m_openButton->setText(i18n("Load")); } if(allHidden && tooltip) tooltip->hide(); if(needResize && tooltip) tooltip->resize(tooltip->sizeHint()); } void WorkingSetToolTipWidget::buttonClicked(bool) { QPointer stillExists(this); auto* s = qobject_cast(sender()); Q_ASSERT(s); auto* mainWindow = qobject_cast(Core::self()->uiController()->activeMainWindow()); Q_ASSERT(mainWindow); - QSet openFiles = Core::self()->workingSetControllerInternal()->workingSet(mainWindow->area()->workingSet())->fileList().toSet(); + QSet openFiles = Core::self()->workingSetControllerInternal()->workingSet(mainWindow->area()->workingSet())->fileSet(); if(!openFiles.contains(s->objectName())) { Core::self()->documentControllerInternal()->openDocument(QUrl::fromUserInput(s->objectName())); }else{ openFiles.remove(s->objectName()); filterViews(openFiles); } if(stillExists) updateFileButtons(); } void WorkingSetToolTipWidget::labelClicked() { QPointer stillExists(this); auto* s = qobject_cast(sender()); Q_ASSERT(s); bool found = false; auto* window = static_cast(ICore::self()->uiController()->activeMainWindow()); const auto views = window->area()->views(); for (Sublime::View* view : views) { if(view->document()->documentSpecifier() == s->objectName()) { window->activateView(view); found = true; break; } } if(!found) Core::self()->documentControllerInternal()->openDocument(QUrl::fromUserInput(s->objectName())); if(stillExists) updateFileButtons(); } #include "workingsettooltipwidget.moc" diff --git a/kdevplatform/sublime/mainwindow_p.h b/kdevplatform/sublime/mainwindow_p.h index a0d59583ac..27a2e07adb 100644 --- a/kdevplatform/sublime/mainwindow_p.h +++ b/kdevplatform/sublime/mainwindow_p.h @@ -1,162 +1,169 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEMAINWINDOW_P_H #define KDEVPLATFORM_SUBLIMEMAINWINDOW_P_H #include #include #include #include #include "area.h" #include "sublimedefs.h" #include "mainwindow.h" #include class QAction; class QSplitter; class IdealToolBar; namespace Sublime { class View; class Container; class Controller; class AreaIndex; class IdealMainWidget; class IdealController; class MessageWidget; class Message; class MainWindowPrivate: public QObject { Q_OBJECT public: MainWindowPrivate(MainWindow *w, Controller* controller); ~MainWindowPrivate() override; /**Use this to create tool views for an area.*/ class IdealToolViewCreator { public: explicit IdealToolViewCreator(MainWindowPrivate *_d): d(_d) {} Area::WalkerMode operator() (View *view, Sublime::Position position); private: MainWindowPrivate* const d; }; /**Use this to create views for an area.*/ class ViewCreator { public: - explicit ViewCreator(MainWindowPrivate *_d, const QList& _topViews = QList()): d(_d), topViews(_topViews.toSet()) {} + explicit ViewCreator(MainWindowPrivate *_d, const QList& _topViews = QList()) + : d(_d) +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + , topViews(_topViews.begin(), _topViews.end()) +#else + , topViews(_topViews.toSet()) +#endif + {} Area::WalkerMode operator() (AreaIndex *index); private: MainWindowPrivate* const d; const QSet topViews; }; /**Reconstructs the mainwindow according to the current area.*/ void reconstruct(); /**Reconstructs the views according to the current area index.*/ void reconstructViews(const QList& topViews = QList()); /**Clears the area leaving mainwindow empty.*/ void clearArea(); /** Sets a @p w widget that will be shown when there are no documents on the area */ void setBackgroundCentralWidget(QWidget* w); void activateFirstVisibleView(); Controller* const controller; Area *area; QList docks; QMap viewContainers; QMap widgetToView; View *activeView; View *activeToolView; QWidget *centralWidget; QWidget* bgCentralWidget; MessageWidget* messageWidget; ViewBarContainer* viewBarContainer; QSplitter* splitterCentralWidget; IdealController *idealController; bool ignoreDockShown; bool autoAreaSettingsSave; bool eventFilter(QObject* obj, QEvent* event) override; void disableConcentrationMode(); void postMessage(Message* message); public Q_SLOTS: void toggleDocksShown(); void viewAdded(Sublime::AreaIndex *index, Sublime::View *view); void viewRemovedInternal(Sublime::AreaIndex *index, Sublime::View *view); void raiseToolView(Sublime::View* view); void aboutToRemoveView(Sublime::AreaIndex *index, Sublime::View *view); void toolViewAdded(Sublime::View *toolView, Sublime::Position position); void aboutToRemoveToolView(Sublime::View *toolView, Sublime::Position position); void toolViewMoved(Sublime::View *toolView, Sublime::Position position); void setTabBarLeftCornerWidget(QWidget* widget); private Q_SLOTS: void updateAreaSwitcher(Sublime::Area *area); void slotDockShown(Sublime::View*, Sublime::Position, bool); void widgetCloseRequest(QWidget* widget); void showLeftDock(bool b); void showRightDock(bool b); void showBottomDock(bool b); void focusEditor(); void selectNextDock(); void selectPreviousDock(); void messageDestroyed(Message* message); private: void restoreConcentrationMode(); void setBackgroundVisible(bool v); Qt::DockWidgetArea positionToDockArea(Position position); void cleanCentralWidget(); MainWindow* const m_mainWindow; // uses QPointer to make already-deleted splitters detectable QMap > m_indexSplitters; QMap m_areaActions; QPointer m_leftTabbarCornerWidget; QPointer m_concentrateToolBar; IdealToolBar* m_bottomToolBar; IdealToolBar* m_rightToolBar; IdealToolBar* m_leftToolBar; QAction* m_concentrationModeAction; QHash>> m_messageHash; }; } #endif diff --git a/kdevplatform/sublime/message.h b/kdevplatform/sublime/message.h index 958929a8df..58be1aeed2 100644 --- a/kdevplatform/sublime/message.h +++ b/kdevplatform/sublime/message.h @@ -1,280 +1,280 @@ /* SPDX-License-Identifier: LGPL-2.0-or-later Copyright (C) 2012-2013 Dominik Haumann Copyright (C) 2020 Friedrich W. H. Kossebau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Forked from KTextEditor::Message at v5.66.0 // Dropped Document/View properties, made wordWrap true by default, dropped position & autoHideMode // Should be investigated later to turn this and the messagewidget class // into some reusable generic in-shell-message code, e.g. as KF module // TODO: re-add autoHideMode once there is an idea how to determine user interaction with the shell #ifndef KDEVPLATFORM_SUBLIME_MESSAGE_H #define KDEVPLATFORM_SUBLIME_MESSAGE_H #include "sublimeexport.h" // Qt #include #include #include #include namespace Sublime { /** * @brief This class holds a Message to display in a message area. * * @section message_intro Introduction * * The Message class holds the data used to display interactive message widgets * in the shell. Use the MainWindow::postMessage() to post a message as follows: * * @code * // if you keep a pointer after calling postMessage(), * // always use a QPointer go guard your Message, * QPointer message = * new Sublime::Message("text", Sublime::Message::Information); * message->addAction(...); // add your actions, if any... * window->postMessage(message); * @endcode * * A Message is deleted automatically if the Message gets closed, meaning that * you usually can forget the pointer. If you really need to delete a message * before the user processed it, always guard it with a QPointer! * * @section message_creation Message Creation and Deletion * * To create a new Message, use code like this: * @code * QPointer message = * new Sublime::Message("My information text", Message::Information); * // ... * @endcode * * Although discouraged in general, the text of the Message can be changed * on the fly when it is already visible with setText(). * * Once you posted the Message through MainWindow::postMessage(), the * lifetime depends on the user interaction. The Message gets automatically * deleted if the user clicks a closing action in the message, or the set * timeout is reached. * * If you posted a message but want to remove it yourself again, just delete * the message. But beware of the following warning! * * @warning Always use QPointer\ to guard the message pointer from * getting invalid, if you need to access the Message after you posted * it. * * @section message_hiding Autohiding Messages * * Message%s can be shown for only a short amount of time by using the autohide * feature. With setAutoHide() a timeout in milliseconds can be set after which * the Message is automatically hidden. */ class KDEVPLATFORMSUBLIME_EXPORT Message : public QObject { Q_OBJECT // // public data types // public: /** * Message types used as visual indicator. * The message types match exactly the behavior of KMessageWidget::MessageType. * For simple notifications either use Positive or Information. */ enum MessageType { Positive = 0, ///< positive information message Information, ///< information message type Warning, ///< warning message type Error ///< error message type }; public: /** * Constructor for new messages. * @param type the message type, e.g. MessageType::Information * @param richtext text to be displayed */ - Message(const QString &richtext, MessageType type = Message::Information); + explicit Message(const QString& richtext, MessageType type = Message::Information); /** * Destructor. */ ~Message() override; public: /** * Returns the text set in the constructor. */ QString text() const; /** * Returns the icon of this message. * If the message has no icon set, a null icon is returned. * @see setIcon() */ QIcon icon() const; /** * Returns the message type set in the constructor. */ MessageType messageType() const; /** * Adds an action to the message. * * By default (@p closeOnTrigger = true), the action closes the message * displayed. If @p closeOnTrigger is @e false, the message is stays open. * * The actions will be displayed in the order you added the actions. * * To connect to an action, use the following code: * @code * connect(action, &QAction::triggered, receiver, &ReceiverType::slotActionTriggered); * @endcode * * @param action action to be added * @param closeOnTrigger when triggered, the message widget is closed * * @warning The added actions are deleted automatically. * So do @em not delete the added actions yourself. */ void addAction(QAction* action, bool closeOnTrigger = true); /** * Accessor to all actions, mainly used in the internal implementation * to add the actions into the gui. * @see addAction() */ QVector actions() const; /** * Set the auto hide time to @p delay milliseconds. * If @p delay < 0, auto hide is disabled. * If @p delay = 0, auto hide is enabled and set to a sane default * value of several seconds. * * By default, auto hide is disabled. * * @see autoHide() */ void setAutoHide(int delay = 0); /** * Returns the auto hide time in milliseconds. * Please refer to setAutoHide() for an explanation of the return value. * * @see setAutoHide() */ int autoHide() const; /** * Enabled word wrap according to @p wordWrap. * By default, auto wrap is enabled. * * Word wrap is enabled automatically, if the Message's width is larger than * the parent widget's width to avoid breaking the gui layout. * * @see wordWrap() */ void setWordWrap(bool wordWrap); /** * Check, whether word wrap is enabled or not. * * @see setWordWrap() */ bool wordWrap() const; /** * Set the priority of this message to @p priority. * Messages with higher priority are shown first. * The default priority is 0. * * @see priority() */ void setPriority(int priority); /** * Returns the priority of the message. * * @see setPriority() */ int priority() const; public Q_SLOTS: /** * Sets the notification contents to @p richtext. * If the message was already sent through MainWindow::postMessage(), * the displayed text changes on the fly. * @note Change text on the fly with care, since changing the text may * resize the notification widget, which may result in a distracting * user experience. * @param richtext new notification text (rich text supported) * @see textChanged() */ void setText(const QString& richtext); /** * Add an optional @p icon for this notification which will be shown next to * the message text. If the message was already sent through MainWindow::postMessage(), * the displayed icon changes on the fly. * @note Change the icon on the fly with care, since changing the text may * resize the notification widget, which may result in a distracting * user experience. * @param icon the icon to be displayed * @see iconChanged() */ void setIcon(const QIcon& icon); Q_SIGNALS: /** * This signal is emitted before the @p message is deleted. Afterwards, this * pointer is invalid. * * @param message the closed/processed message */ void closed(Message* message); /** * This signal is emitted whenever setText() was called. * * @param text the new notification text (rich text supported) * @see setText() */ void textChanged(const QString& text); /** * This signal is emitted whenever setIcon() was called. * @param icon the new notification icon * @see setIcon() */ void iconChanged(const QIcon& icon); private: const QScopedPointer d; }; } #endif diff --git a/kdevplatform/util/environmentselectionmodel.cpp b/kdevplatform/util/environmentselectionmodel.cpp index e37592f149..a4b80742f6 100644 --- a/kdevplatform/util/environmentselectionmodel.cpp +++ b/kdevplatform/util/environmentselectionmodel.cpp @@ -1,111 +1,121 @@ /* This file is part of KDevelop Copyright 2013 Ivan Shapovalov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "environmentselectionmodel.h" #include namespace { QStringList entriesFromEnv(const KDevelop::EnvironmentProfileList& env) { // We add an empty (i. e. default profile) entry to the front of the model's list. return QStringList(QString()) + env.profileNames(); } } namespace KDevelop { EnvironmentSelectionModel::EnvironmentSelectionModel(QObject* parent) : QStringListModel(parent) , m_env(KSharedConfig::openConfig()) { - setStringList(entriesFromEnv(m_env)); - m_profilesLookupTable = stringList().toSet(); + const QStringList entries = entriesFromEnv(m_env); + setStringList(entries); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + m_profilesLookupTable = QSet(entries.begin(), entries.end()); +#else + m_profilesLookupTable = entries.toSet(); +#endif } QVariant EnvironmentSelectionModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section != 0 || orientation != Qt::Horizontal || role != Qt::DisplayRole) { return QVariant(); } return i18nc("@title:column", "Profile"); } QVariant EnvironmentSelectionModel::data(const QModelIndex& index, int role) const { QVariant nativeData = QStringListModel::data(index, Qt::DisplayRole); QString profileName = nativeData.toString(); switch (role) { case Qt::DisplayRole: if (profileName.isEmpty()) { return i18nc("@item:inlistbox", "Use default profile (currently: %1)", m_env.defaultProfileName()); } if (!m_profilesLookupTable.contains(profileName)) { return i18nc("@item:inlistbox", "%1 (does not exist)", profileName); } return nativeData; case EffectiveNameRole: if (profileName.isEmpty()) { return m_env.defaultProfileName(); } return nativeData; default: return QStringListModel::data(index, role); } } bool EnvironmentSelectionModel::setData(const QModelIndex& /*index*/, const QVariant& /*value*/, int /*role*/) { return false; } EnvironmentProfileList EnvironmentSelectionModel::environmentProfiles() const { return m_env; } void EnvironmentSelectionModel::reload() { m_env = EnvironmentProfileList(KSharedConfig::openConfig()); - setStringList(entriesFromEnv(m_env)); - m_profilesLookupTable = stringList().toSet(); + const QStringList entries = entriesFromEnv(m_env); + setStringList(entries); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + m_profilesLookupTable = QSet(entries.begin(), entries.end()); +#else + m_profilesLookupTable = entries.toSet(); +#endif } QString EnvironmentSelectionModel::reloadSelectedItem(const QString& currentProfile) { if (m_profilesLookupTable.contains(currentProfile)) { return currentProfile; } else { return QString(); } } } // namespace KDevelop diff --git a/kdevplatform/vcs/models/vcsfilechangesmodel.cpp b/kdevplatform/vcs/models/vcsfilechangesmodel.cpp index 1507cece59..061595e1ca 100644 --- a/kdevplatform/vcs/models/vcsfilechangesmodel.cpp +++ b/kdevplatform/vcs/models/vcsfilechangesmodel.cpp @@ -1,310 +1,314 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Split into separate class Copyright 2011 Andrey Batyiev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vcsfilechangesmodel.h" #include "debug.h" #include #include #include #include #include #include namespace KDevelop { static QString stateToString(KDevelop::VcsStatusInfo::State state) { switch(state) { case KDevelop::VcsStatusInfo::ItemAdded: return i18nc("file was added to versioncontrolsystem", "Added"); case KDevelop::VcsStatusInfo::ItemDeleted: return i18nc("file was deleted from versioncontrolsystem", "Deleted"); case KDevelop::VcsStatusInfo::ItemHasConflicts: return i18nc("file is conflicting (versioncontrolsystem)", "Has Conflicts"); case KDevelop::VcsStatusInfo::ItemModified: return i18nc("version controlled file was modified", "Modified"); case KDevelop::VcsStatusInfo::ItemUpToDate: return i18nc("file is up to date in versioncontrolsystem", "Up To Date"); case KDevelop::VcsStatusInfo::ItemUnknown: case KDevelop::VcsStatusInfo::ItemUserState: return i18nc("file is not known to versioncontrolsystem", "Unknown"); } return i18nc("Unknown VCS file status, probably a backend error", "?"); } static QIcon stateToIcon(KDevelop::VcsStatusInfo::State state) { switch(state) { case KDevelop::VcsStatusInfo::ItemAdded: return QIcon::fromTheme(QStringLiteral("vcs-added")); case KDevelop::VcsStatusInfo::ItemDeleted: return QIcon::fromTheme(QStringLiteral("vcs-removed")); case KDevelop::VcsStatusInfo::ItemHasConflicts: return QIcon::fromTheme(QStringLiteral("vcs-conflicting")); case KDevelop::VcsStatusInfo::ItemModified: return QIcon::fromTheme(QStringLiteral("vcs-locally-modified")); case KDevelop::VcsStatusInfo::ItemUpToDate: return QIcon::fromTheme(QStringLiteral("vcs-normal")); case KDevelop::VcsStatusInfo::ItemUnknown: case KDevelop::VcsStatusInfo::ItemUserState: return QIcon::fromTheme(QStringLiteral("unknown")); } return QIcon::fromTheme(QStringLiteral("dialog-error")); } VcsFileChangesSortProxyModel::VcsFileChangesSortProxyModel(QObject* parent) : QSortFilterProxyModel(parent) { } bool VcsFileChangesSortProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const { const auto leftStatus = source_left.data(VcsFileChangesModel::StateRole).value(); const auto rightStatus = source_right.data(VcsFileChangesModel::StateRole).value(); if (leftStatus != rightStatus) { return leftStatus < rightStatus; } const QString leftPath = source_left.data(VcsFileChangesModel::UrlRole).toString(); const QString rightPath = source_right.data(VcsFileChangesModel::UrlRole).toString(); return QString::localeAwareCompare(leftPath, rightPath) < 0; } class VcsStatusInfoItem : public QStandardItem { public: explicit VcsStatusInfoItem(const VcsStatusInfo& info) : QStandardItem() , m_info(info) {} void setStatus(const VcsStatusInfo& info) { m_info = info; emitDataChanged(); } QVariant data(int role) const override { switch(role) { case Qt::DisplayRole: return stateToString(m_info.state()); case Qt::DecorationRole: return stateToIcon(m_info.state()); case VcsFileChangesModel::VcsStatusInfoRole: return QVariant::fromValue(m_info); case VcsFileChangesModel::UrlRole: return m_info.url(); case VcsFileChangesModel::StateRole: return QVariant::fromValue(m_info.state()); } return {}; } private: VcsStatusInfo m_info; }; class VcsFileChangesModelPrivate { public: bool allowSelection; }; VcsFileChangesModel::VcsFileChangesModel(QObject *parent, bool allowSelection) : QStandardItemModel(parent) , d_ptr(new VcsFileChangesModelPrivate{allowSelection}) { setColumnCount(2); setHeaderData(0, Qt::Horizontal, i18n("Filename")); setHeaderData(1, Qt::Horizontal, i18n("Status")); } VcsFileChangesModel::~VcsFileChangesModel() { } int VcsFileChangesModel::updateState(QStandardItem *parent, const KDevelop::VcsStatusInfo &status) { Q_D(VcsFileChangesModel); if(status.state()==VcsStatusInfo::ItemUnknown || status.state()==VcsStatusInfo::ItemUpToDate) { removeUrl(status.url()); return -1; } else { QStandardItem* item = fileItemForUrl(parent, status.url()); if(!item) { QString path = ICore::self()->projectController()->prettyFileName(status.url(), KDevelop::IProjectController::FormatPlain); QMimeType mime = status.url().isLocalFile() ? QMimeDatabase().mimeTypeForFile(status.url().toLocalFile(), QMimeDatabase::MatchExtension) : QMimeDatabase().mimeTypeForUrl(status.url()); QIcon icon = QIcon::fromTheme(mime.iconName()); item = new QStandardItem(icon, path); auto itStatus = new VcsStatusInfoItem(status); if(d->allowSelection) { item->setCheckable(true); item->setCheckState(status.state() == VcsStatusInfo::ItemUnknown ? Qt::Unchecked : Qt::Checked); } parent->appendRow({ item, itStatus }); } else { QStandardItem *parent = item->parent(); if(parent == nullptr) parent = invisibleRootItem(); auto statusInfoItem = static_cast(parent->child(item->row(), 1)); statusInfoItem->setStatus(status); } return item->row(); } } QVariant VcsFileChangesModel::data(const QModelIndex &index, int role) const { if (role >= VcsStatusInfoRole && index.column()==0) { return QStandardItemModel::data(index.sibling(index.row(), 1), role); } return QStandardItemModel::data(index, role); } QStandardItem* VcsFileChangesModel::fileItemForUrl(QStandardItem* parent, const QUrl& url) const { Q_ASSERT(parent); if (!parent) { qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO; return nullptr; } for(int i=0, c=parent->rowCount(); ichild(i); if(indexFromItem(item).data(UrlRole).toUrl() == url) { return parent->child(i); } } return nullptr; } void VcsFileChangesModel::setAllChecked(bool checked) { Q_D(VcsFileChangesModel); if(!d->allowSelection) return; QStandardItem* parent = invisibleRootItem(); for(int i = 0, c = parent->rowCount(); i < c; i++) { QStandardItem* item = parent->child(i); item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); } } QList VcsFileChangesModel::checkedUrls(QStandardItem *parent) const { Q_D(const VcsFileChangesModel); Q_ASSERT(parent); if (!parent) { qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO; return {}; } QList ret; for(int i = 0, c = parent->rowCount(); i < c; i++) { QStandardItem* item = parent->child(i); if(!d->allowSelection || item->checkState() == Qt::Checked) { ret << indexFromItem(item).data(UrlRole).toUrl(); } } return ret; } QList VcsFileChangesModel::urls(QStandardItem *parent) const { Q_ASSERT(parent); if (!parent) { qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO; return {}; } QList ret; const int c = parent->rowCount(); ret.reserve(c); for (int i = 0; i < c; i++) { QStandardItem* item = parent->child(i); ret << indexFromItem(item).data(UrlRole).toUrl(); } return ret; } void VcsFileChangesModel::checkUrls(QStandardItem *parent, const QList& urls) const { Q_D(const VcsFileChangesModel); Q_ASSERT(parent); if (!parent) { qCWarning(VCS) << "null QStandardItem passed to" << Q_FUNC_INFO; return; } if(!d->allowSelection) return; - QSet urlSet(urls.toSet()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const QSet urlSet(urls.begin(), urls.end()); +#else + const QSet urlSet(urls.toSet()); +#endif for(int i = 0, c = parent->rowCount(); i < c; i++) { QStandardItem* item = parent->child(i); item->setCheckState(urlSet.contains(indexFromItem(item).data(UrlRole).toUrl()) ? Qt::Checked : Qt::Unchecked); } } void VcsFileChangesModel::setIsCheckbable(bool checkable) { Q_D(VcsFileChangesModel); d->allowSelection = checkable; } bool VcsFileChangesModel::isCheckable() const { Q_D(const VcsFileChangesModel); return d->allowSelection; } bool VcsFileChangesModel::removeUrl(const QUrl& url) { const auto matches = match(index(0, 0), UrlRole, url, 1, Qt::MatchExactly); if (matches.isEmpty()) return false; const auto& idx = matches.first(); return removeRow(idx.row(), idx.parent()); } } diff --git a/kdevplatform/vcs/widgets/vcsannotationitemdelegate.cpp b/kdevplatform/vcs/widgets/vcsannotationitemdelegate.cpp index f7da050267..fd5b451014 100644 --- a/kdevplatform/vcs/widgets/vcsannotationitemdelegate.cpp +++ b/kdevplatform/vcs/widgets/vcsannotationitemdelegate.cpp @@ -1,404 +1,402 @@ /* This file is part of KDevelop * * Copyright 2017-2018 Friedrich W. H. Kossebau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "vcsannotationitemdelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; VcsAnnotationItemDelegate::VcsAnnotationItemDelegate(KTextEditor::View* view, KTextEditor::AnnotationModel* model, QObject* parent) : KTextEditor::AbstractAnnotationItemDelegate(parent) , m_model(model) { // dump background brushes on schema change Q_ASSERT(qobject_cast(view)); connect(view, SIGNAL(configChanged()), this, SLOT(resetBackgrounds())); view->installEventFilter(this); } VcsAnnotationItemDelegate::~VcsAnnotationItemDelegate() = default; static QString ageOfDate(const QDate& date) { const auto now = QDate::currentDate(); int ageInYears = now.year() - date.year(); if (now < date.addYears(ageInYears)) { --ageInYears; } if (ageInYears > 0) { return i18ncp("age", "%1 year", "%1 years", ageInYears); } int ageInMonths = now.month() - date.month(); if (now.day() < date.day()) { --ageInMonths; } if (ageInMonths < 0) { ageInMonths += 12; } if (ageInMonths > 0) { return i18ncp("age", "%1 month", "%1 months", ageInMonths); } const int ageInDays = date.daysTo(now); if (ageInDays > 0) { return i18ncp("age", "%1 day", "%1 days", ageInDays); } return i18n("Today"); } void VcsAnnotationItemDelegate::doMessageLineLayout(const KTextEditor::StyleOptionAnnotationItem& option, QRect* messageRect, QRect* ageRect) const { Q_ASSERT(messageRect && messageRect->isValid()); Q_ASSERT(ageRect); const QWidget* const widget = option.view; QStyle* const style = widget ? widget->style() : QApplication::style(); const bool hasAge = ageRect->isValid(); // "+ 1" as used in QItemDelegate const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1; const int ageMargin = hasAge ? textMargin : 0; const int x = option.rect.left(); const int y = option.rect.top(); const int w = option.rect.width(); const int h = option.rect.height(); // add margins for fixed elements QSize ageSize(0, 0); // ageRect could be invalid, so use separate object for calculation if (hasAge) { ageSize = ageRect->size(); ageSize.rwidth() += 2 * ageMargin; } // distribute space among layout items QRect message; QRect age; if (option.direction == Qt::LeftToRight) { message.setRect(x, y, w - ageSize.width(), h); age.setRect(message.right() + 1, y, ageSize.width(), h); } else { age.setRect(x, y, ageSize.width(), h); message.setRect(age.right() + 1, y, w - ageSize.width(), h); } // remove margins here, so renderMessageAndAge does not have to message.adjust(textMargin, 0, -textMargin, 0); age.adjust(ageMargin, 0, -ageMargin, 0); // return result *ageRect = age; *messageRect = QStyle::alignedRect(option.direction, Qt::AlignLeading, messageRect->size().boundedTo(message.size()), message); } void VcsAnnotationItemDelegate::doAuthorLineLayout(const KTextEditor::StyleOptionAnnotationItem& option, QRect* authorRect) const { Q_ASSERT(authorRect); // if invalid, nothing to be done, keep as is if (!authorRect->isValid()) { return; } const QWidget* const widget = option.view; QStyle* const style = widget ? widget->style() : QApplication::style(); // "+ 1" as used in QItemDelegate const int authorMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1; QRect author = option.rect; // remove margins here, so renderAuthor does not have to author.adjust(authorMargin, 0, -authorMargin, 0); // return result *authorRect = QStyle::alignedRect(option.direction, Qt::AlignLeading, authorRect->size().boundedTo(author.size()), author); } void VcsAnnotationItemDelegate::renderBackground(QPainter* painter, const KTextEditor::StyleOptionAnnotationItem& option, const VcsAnnotationLine& annotationLine) const { Q_UNUSED(option); const auto revision = annotationLine.revision(); auto brushIt = m_backgrounds.find(revision); if (brushIt == m_backgrounds.end()) { KTextEditor::Attribute::Ptr normalStyle = option.view->defaultStyleAttribute(KTextEditor::dsNormal); const auto background = (normalStyle->hasProperty(QTextFormat::BackgroundBrush)) ? normalStyle->background().color() : QColor(Qt::white); const int background_y = background.red()*0.299 + 0.587*background.green() + 0.114*background.blue(); // get random, but reproducable 8-bit values from last two bytes of the revision hash const uint revisionHash = qHash(revision); const int u = static_cast((0xFF & revisionHash)); const int v = static_cast((0xFF00 & revisionHash) >> 8); const int r = qRound(qMin(255.0, qMax(0.0, background_y + 1.402*(v-128)))); const int g = qRound(qMin(255.0, qMax(0.0, background_y - 0.344*(u-128) - 0.714*(v-128)))); const int b = qRound(qMin(255.0, qMax(0.0, background_y + 1.772*(u-128)))); brushIt = m_backgrounds.insert(revision, QBrush(QColor(r, g, b))); } painter->fillRect(option.rect, brushIt.value()); } void VcsAnnotationItemDelegate::renderMessageAndAge(QPainter* painter, const KTextEditor::StyleOptionAnnotationItem& option, const QRect& messageRect, const QString& messageText, const QRect& ageRect, const QString& ageText) const { Q_UNUSED(option); painter->save(); KTextEditor::Attribute::Ptr normalStyle = option.view->defaultStyleAttribute(KTextEditor::dsNormal); painter->setPen(normalStyle->foreground().color()); painter->drawText(messageRect, Qt::AlignLeading | Qt::AlignVCenter, painter->fontMetrics().elidedText(messageText, Qt::ElideRight, messageRect.width())); // TODO: defaultStyleAttribute only returns reliably for dsNormal, so what to do for a comment-like color? KTextEditor::Attribute::Ptr commentStyle = option.view->defaultStyleAttribute(KTextEditor::dsNormal); painter->setPen(commentStyle->foreground().color()); painter->drawText(ageRect, Qt::AlignTrailing | Qt::AlignVCenter, ageText); painter->restore(); } void VcsAnnotationItemDelegate::renderAuthor(QPainter* painter, const KTextEditor::StyleOptionAnnotationItem& option, const QRect& authorRect, const QString& authorText) const { Q_UNUSED(option); painter->save(); // TODO: defaultStyleAttribute only returns reliably for dsNormal, so what to do for a comment-like color? KTextEditor::Attribute::Ptr commentStyle = option.view->defaultStyleAttribute(KTextEditor::dsNormal); painter->setPen(commentStyle->foreground().color()); painter->drawText(authorRect, Qt::AlignLeading | Qt::AlignVCenter, painter->fontMetrics().elidedText(authorText, Qt::ElideRight, authorRect.width())); painter->restore(); } void VcsAnnotationItemDelegate::renderHighlight(QPainter* painter, const KTextEditor::StyleOptionAnnotationItem& option) const { // Draw a border around all adjacent entries that have the same text as the currently hovered one if ((option.state & QStyle::State_MouseOver) && (option.annotationItemGroupingPosition & KTextEditor::StyleOptionAnnotationItem::InGroup)) { KTextEditor::Attribute::Ptr style = option.view->defaultStyleAttribute(KTextEditor::dsNormal); painter->setPen(style->foreground().color()); // Use floating point coordinates to support scaled rendering QRectF rect(option.rect); rect.adjust(0.5, 0.5, -0.5, -0.5); // draw left and right highlight borders painter->drawLine(rect.topLeft(), rect.bottomLeft()); painter->drawLine(rect.topRight(), rect.bottomRight()); if ((option.annotationItemGroupingPosition & KTextEditor::StyleOptionAnnotationItem::GroupBegin) && (option.wrappedLine == 0)) { painter->drawLine(rect.topLeft(), rect.topRight()); } if ((option.annotationItemGroupingPosition & KTextEditor::StyleOptionAnnotationItem::GroupEnd) && (option.wrappedLine == (option.wrappedLineCount-1))) { painter->drawLine(rect.bottomLeft(), rect.bottomRight()); } } } void VcsAnnotationItemDelegate::paint(QPainter* painter, const KTextEditor::StyleOptionAnnotationItem& option, KTextEditor::AnnotationModel* model, int line) const { Q_ASSERT(painter); // we cannot use custom roles and data() API (cmp. VcsAnnotationModel dox), so accessing custom API instead VcsAnnotationModel* vcsModel = qobject_cast(model); Q_ASSERT(vcsModel); if (!painter || !vcsModel) { return; } // test of line just for sake of completeness skipped here // Fetch data from the model const VcsAnnotationLine annotationLine = vcsModel->annotationLine(line); if (annotationLine.revision().revisionType() == VcsRevision::Invalid) { return; } // prepare painter->save(); renderBackground(painter, option, annotationLine); // We use the normal UI font here, which usually is a proportimal one, // so more text fits into the available space. // Though we do this at the cost of not adapting to any scaled content font size, // as there is no zooming state info available, so we cannot adapt. // Tooltip font also is not scaled, and annotations could be considered to fall into // that category, so might be fine. painter->setFont(option.view->font()); if (option.visibleWrappedLineInGroup == 0) { QRect ageRect; QString ageText; const auto date = annotationLine.date(); if (date.isValid()) { ageText = ageOfDate(date.date()); ageRect = QRect(QPoint(0, 0), #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) QSize(option.fontMetrics.horizontalAdvance(ageText), option.rect.height())); #else QSize(option.fontMetrics.width(ageText), option.rect.height())); #endif } const auto messageText = annotationLine.commitMessage(); auto messageRect = QRect(QPoint(0, 0), #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) QSize(option.fontMetrics.horizontalAdvance(messageText), option.rect.height())); #else QSize(option.fontMetrics.width(messageText), option.rect.height())); #endif doMessageLineLayout(option, &messageRect, &ageRect); renderMessageAndAge(painter, option, messageRect, messageText, ageRect, ageText); } else if (option.visibleWrappedLineInGroup == 1) { const auto author = annotationLine.author(); if (!author.isEmpty()) { const auto authorText = i18nc("By: commit author", "By: %1", author); auto authorRect = QRect(QPoint(0, 0), #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) QSize(option.fontMetrics.horizontalAdvance(authorText), option.rect.height())); #else QSize(option.fontMetrics.width(authorText), option.rect.height())); #endif doAuthorLineLayout(option, &authorRect); renderAuthor(painter, option, authorRect, authorText); } } renderHighlight(painter, option); // done painter->restore(); } bool VcsAnnotationItemDelegate::helpEvent(QHelpEvent* event, KTextEditor::View* view, const KTextEditor::StyleOptionAnnotationItem& option, KTextEditor::AnnotationModel* model, int line) { Q_UNUSED(option); if (!model || event->type() != QEvent::ToolTip) { return false; } - const QString annotationGroupId = model->data(line, (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole).toString(); - const QVariant data = model->data(line, Qt::ToolTipRole); if (!data.isValid()) { return false; } const QString toolTipText = data.toString(); if (toolTipText.isEmpty()) { return false; } QToolTip::showText(event->globalPos(), toolTipText, view, option.rect); return true; } void VcsAnnotationItemDelegate::hideTooltip(KTextEditor::View *view) { Q_UNUSED(view); QToolTip::hideText(); } QSize VcsAnnotationItemDelegate::sizeHint(const KTextEditor::StyleOptionAnnotationItem& option, KTextEditor::AnnotationModel* model, int line) const { Q_UNUSED(line); Q_ASSERT(model); if (!model) { return QSize(0, 0); } // Ideally the user could configure the width of the annotations, best interactively. // Until this is possible, the sizehint is: roughly 40 chars, but maximal 25 % of the view // See eventFilter for making sure we adapt the max 25 % to a changed width. const QFontMetricsF& fm(option.fontMetrics); // if only averageCharWidth would yield sane values, // multiply by 40 in average seemed okayish at least with english, showing enough of message m_lastCharBasedWidthHint = ceil(40 * fm.averageCharWidth()); m_lastViewBasedWidthHint = widthHintFromViewWidth(option.view->width()); return QSize(qMin(m_lastCharBasedWidthHint, m_lastViewBasedWidthHint), fm.height()); } bool VcsAnnotationItemDelegate::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::Resize) { auto resizeEvent = static_cast(event); const int viewBasedWidthHint = widthHintFromViewWidth(resizeEvent->size().width()); if ((viewBasedWidthHint < m_lastCharBasedWidthHint) && (viewBasedWidthHint != m_lastViewBasedWidthHint)) { // emit for first line only, assuming uniformAnnotationItemSizes is set to true emit sizeHintChanged(m_model, 0); } } return KTextEditor::AbstractAnnotationItemDelegate::eventFilter(object, event); } void VcsAnnotationItemDelegate::resetBackgrounds() { m_backgrounds.clear(); } int VcsAnnotationItemDelegate::widthHintFromViewWidth(int viewWidth) const { return viewWidth * m_maxWidthViewPercent / 100; } diff --git a/org.kde.kdevelop.appdata.xml b/org.kde.kdevelop.appdata.xml index 2cb89e981b..417ace0eec 100644 --- a/org.kde.kdevelop.appdata.xml +++ b/org.kde.kdevelop.appdata.xml @@ -1,158 +1,147 @@ org.kde.kdevelop.desktop CC0-1.0 GPL-2.0+ KDevelop - مطوّرك KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop KDevelop xxKDevelopxx KDevelop KDevelop Featureful, plugin-extensible IDE for C/C++ and other programming languages - بيئة تطوير متكاملة بمزايا عديدة ودعم للملحقات للغتي سي/سي++ ولغات البرمجة الأخرى És un IDE, extensible amb connectors, amb totes les característiques per a C/C++ i altres llenguatges de programació. És un IDE, extensible amb connectors, amb totes les característiques per a C/C++ i altres llenguatges de programació. Eine leistungsfähige integrierte Entwicklungsumgebung (IDE) für C/C++ und andere Programmiersprachen, die durch Module erweitert werden kann. Featureful, plugin-extensible IDE for C/C++ and other programming languages Entorno de desarrollo integrado para C/C++ y otros lenguajes de programación con múltiples funcionalidades y que se puede extender con complementos. - C/C++ ja teiste programmeerimiskeelte omadusterohke ning pluginatega laiendatav IDE Environnement de développement complet et extensible pour le C/C++ et d'autres langages de programmation. Potente ambiente de desenvolvemento integrado para C, C++ e outras linguaxes de programación. As súas funcionalidades poden estenderse mediante complementos Penuh fitur, IDE plugin yang dapat diekstensi C/C++ dan bahasa pemrograman lain IDE per C/C++ e altri linguaggi di programmazione completo ed estensibile C/C++ 및 기타 프로그래밍 언어를 위한 기능적이고 확장 가능한 IDE IDE voor C/C++ en andere programmeertalen, vol functies en uit te breiden met plug-ins. Jest to w pełni funkcjonalne, z możliwością rozszerzenia przy użyciu wtyczek, zintegrowane środowisko programistyczne dla C/C++ i innych języków programowania. IDE pleno de funcionalidades e modular para o C/C++ e outras linguagens de programação IDE repleto de funcionalidades e expansível através de plugins, para C/C++ e outras linguagens de programação. Plne funkčné, pluginmi rozšíriteľné IDE pre C/C++ a iné programovacie jazyky - Zmogljivo integrirano razvojno okolje za C/C++ in druge programske jezike, ki je razširljivo z vstavki. Funktionsrik, integrerad utvecklingsmiljö för C/C++ och andra programspråk Tam özellikli, eklenti ile geliştirilebilir C/C++ ve diğer programlama dilleri için tümleşik geliştirme ortamı. Це повноцінне, розширюване за допомогою додатків комплексне середовище розробки мовами C/C++ та іншими мовами програмування. xxFeatureful, plugin-extensible IDE for C/C++ and other programming languagesxx 全功能,可扩展的支持 C/C++ 和其他语言的集成开发环境。

KDevelop is a Free and Open Source integrated development environment (IDE).

-

«مطوّرك» هي بيئة تطوير متكاملة (IDE) حرّة ومفتوحة المصدر.

El KDevelop és un entorn de desenvolupament integrat (IDE) lliure i de codi obert.

El KDevelop és un entorn de desenvolupament integrat (IDE) lliure i de codi obert.

KDevelop ist eine integrierte Entwicklungsumgebung (Frei und Open Source).

KDevelop is a Free and Open Source integrated development environment (IDE).

KDevelop es un entorno de desarrollo integrado (IDE) libre y de código abierto.

-

KDevelop on vaba ja avatud lähtekoodiga lõimitud arenduskeskkond (IDE)

KDevelop est un environnement de développement intégré (IDE) libre et en open source.

KDevelop é un ambiente integrado de desenvolvemento (IDE) libre e de código aberto.

KDevelop adalah sebuah IDE (integrated development environment) lingkungan pengembangan terintegrasi yang Gratis dan Bebas Terbuka.

KDevelop è un ambiente di sviluppo integrato (IDE) libero e open source.

KDevelop은 자유 오픈 소스 통합 개발 환경(IDE)입니다.

KDevelop is een vrije geïntegreerde ontwikkelomgeving (IDE) binnen het open source principe.

KDevelop to darmowe i otwartoźródłowe zintegrowane środkowisko programistyczne (IDE).

O KDevelop é um ambiente de desenvolvimento integrado (IDE) livre e em código aberto.

Ambiente de Desenvolvimento Integrado (IDE) em Software Livre e Código Aberto.

KDevelop je slobodné a otvorené integrované vývojové prostredie (IDE).

-

KDevelop je prosto in odprtokodno integrirano razvojno okolje (IDE).

KDevelop är en fri integrerad utvecklingsmiljö (IDE) med öppen källkod för olika programvaruprojekt.

KDevelop Özgür ve Açık Kaynaklı tümleşik geliştirme ortamıdır (IDE).

KDevelop є вільним комплексним середовищем розробки (IDE) з відкритим кодом.

xxKDevelop is a Free and Open Source integrated development environment (IDE).xx

可用于您的不同软件项目的自由和开源的集成开发环境

It provides editing, navigation and debugging features for several programming languages, as well as integration with multiple build systems and version-control systems, using a plugin-based architecture.

-

توفّر البرمجيّة مزايا التّحرير والتّنقّل والتّنقيح لمختلف لغات البرمجة، إلى جانب التّكامل مع مختلف أنظمة البناء وأنظمة التّحكّم بالإصدارات، وذلك عبر البنية المعتمدة على الملحقات.

Proporciona característiques per a l'edició, navegació i depuració per a diversos llenguatges de programació, així com la integració amb múltiples sistemes de construcció i sistemes de control de versions, emprant una arquitectura basada en connectors.

Proporciona característiques per a l'edició, navegació i depuració per a diversos llenguatges de programació, així com la integració amb múltiples sistemes de construcció i sistemes de control de versions, emprant una arquitectura basada en connectors.

Funktionen zum Editieren, Navigieren und zur Fehlersuche für mehrere Programmiersprachen, dazu auch Integration von mehreren Build- und Versionskontrollsystemen auf der Basis einer modularen Architektur.

It provides editing, navigation and debugging features for several programming languages, as well as integration with multiple build systems and version-control systems, using a plugin-based architecture.

Proporciona funciones de edición, navegación y depuración para varios lenguajes de programación, además de integración con diversos sistemas de compilación y de control de versiones, usando una arquitectura basada en complementos.

-

See võimaldab redigeerida, liikuda ja siluda mitmes programmeerimiskeeles ning on pluginapõhise arhitektuuri abil lõiminud mitmeid ehitamis- ja versioonihaldussüsteeme.

Il fournit des fonctionnalités d'édition, navigation et débogage pour plusieurs langages de programmation, ainsi que l'intégration de multiples systèmes de compilation et de gestion de révision, en utilisant une architecture extensible.

Fornece funcionalidades de edición, navegación e depuración para varias linguaxes de programación, así como integración con varios sistemas de construción e sistemas de control de versións, usando unha arquitectura baseada en complementos.

Ini menyediakan fitur pengeditan, navigasi dan debugging untuk beberapa bahasa pemrograman, serta integrasi dengan sistem build multipel dan sistem kontrol versi, menggunakan arsitektur berbasis plugin.

Fornisce funzionalità di modifica, navigazione debug per vari linguaggi di programmazione, così come l'integrazione con vari sistemi di compilazione e di controllo versione, tramite l'uso di architettura basata sulle estensioni.

또한 플러그인 기반 아키텍처를 사용하여 여러 프로그래밍 언어에 대한 편집, 탐색 및 디버깅 기능을 제공하고 여러 빌드 시스템 및 버전 제어 시스템과의 통합도 제공합니다.

Het biedt functies voor bewerking, navigatie en debugging voor verschillende programmeertalen, evenals integratie met meerdere bouwsystemen en versiecontrole systemen, met gebruik van een op plug-ins gebaseerde architectuur.

Zapewnia możliwość edycji, poruszania się po projekcie i diagnozowania błędów dla kilku języków programistycznych, a także integrację z wieloma systemami budowania i zarządzania wersjami poprzez wbudowaną architekturę wtyczek.

Oferece capacidades de edição, navegação e depuração para diversas linguagens de programação, assim como a integração com diversos sistemas de compilação e de controlo de versões, usando uma arquitectura baseada em 'plugins'.

Oferece capacidades de edição, navegação e depuração para diversas linguagens de programação, assim como a integração com diversos sistemas de compilação e de controle de versões, usando uma arquitetura baseada em plugins.

Poskytuje funkcie editovania, navigácie a ladenia pre niektoré programovacie jazyky, ako aj integráciu s viacerými zostavovacími systémami a systémami na správu verzií pomocou pluginovej architektúry.

S pomočjo vstavkov omogoča urejanje, krmarjenje in razhroščevanje za številne programske jezike, hkrati pa ponuja podporo več sistemom za izgradnjo in nadzor različic.

Den tillhandahåller redigerings-, navigerings- och avlusningsfunktioner för flera olika programspråk, samt integrering med ett flertal byggsystem och versionskontrollsystem, med användning av en insticksbaserad arkitektur.

Bir çok programlama dili için düzenleme, gezinme ve hata ayıklama özellikleri sağlar, ayrıca bir çok inşa sistemi ve sürüm kontrol sistemi ile tümleşik olup eklenti-tabanlı mimari kullanır.

У програмі передбачено можливості з редагування, навігації та діагностики коду декількома мовами програмування, а також інтеграцію із декількома системами збирання коду та керування версіями. Використовується архітектура з додатками.

xxIt provides editing, navigation and debugging features for several programming languages, as well as integration with multiple build systems and version-control systems, using a plugin-based architecture.xx

它提供了多种编程语言的编辑、导航和调试功能,以及与多种构建系统和版本控制系统的集成,使用基于插件的体系结构。

KDevelop has parser backends for C, C++ and Javascript/QML, with further external plugins supporting e.g. PHP or Python.

-

لدى «مطوّرك» سندات تحليل للغات سي، وسي++، وجاڤاسكربت/QML كما وملحقات خارجيّة أخرى تدعم PHP أو پيثون.

El KDevelop disposa de dorsals d'analitzador per a C, C ++ i Javascript/QML, amb altres connectors externs que implementen, p. ex., PHP o Python.

El KDevelop disposa de dorsals d'analitzador per a C, C ++ i Javascript/QML, amb altres connectors externs que implementen, p. ex., PHP o Python.

KDevelop beinhaltet Parser-Module für C, C++ und Javascript/QML, weitere externe Module unterstützten zum Beispiel PHP oder Python.

KDevelop has parser backends for C, C++ and Javascript/QML, with further external plugins supporting e.g. PHP or Python.

KDevelop dispone de motores de análisis sintáctico para C, C++ y JavaScript/QML, además de complementos externos (por ejemplo, para PHP o para Python).

-

KDevelopil on C, C++ ja Javasripti/QML parsimise taustaprogrammid, väliste pluginate abil on toetatud ka näiteks PHP ja Python.

KDevelop intègre des parseurs pour le C, le C++ et Javascript/QML, avec des modules externes pour prendre en charge PHP, Python, etc.

KDevelop ten infraestruturas de análise para C, C++ e JavaScript/QML, con complementos externos adicionais que fornecen compatibilidade, por exemplo, con PHP e Python.

KDevelop memiliki pengurai backend untuk C, C++ dan Javascript/QML, dengan mendukung plugin eksternal lebih lanjut misal PHP atau Python.

KDevelop ha dei motori di analisi per C, C++ e JavaScript/QML, come ulteriori estensioni esterne che supportano, ad esempio, PHP o Python.

KDevelop에는 C, C++ 및 Javascript/QML에 대한 파서 백엔드가 있으며, PHP 또는 Python과 같은 추가 외부 플러그인이 지원됩니다.

KDevelop heef backends voor ontleden van C, C++ en Javascript/QML, met verdere externe plug-ins die bijv. PHP of Python ondersteunen.

KDevelop ma silniki do przetwarzania składni C, C++ oraz Javascript/QML, a także inne jak np. PHP lub Python dodawane przy użyciu zewnętrznych wtyczek.

O KDevelop tem infra-estruturas de processamento para C, C++ e Javascript/QML, com mais 'plugins' externos que suportam p.ex. PHP ou Python.

O KDevelop tem infraestruturas de processamento para C, C++ e Javascript/QML, com mais plugins externos que tem suporte a, por exemplo, PHP ou Python.

KDevelop má backendy parsera pre C, C++ a Javascript/QML, s ďalšími externými pluginmi podporujúcimi napr. PHP alebo Python.

KDevelop vsebuje razčlenjevalna zaledja za C, C++ in Javascript/QML, zunanji vstavki pa podpirajo tudi npr. PHP ali Python.

KDevelop har gränssnitt för tolkning av C, C++ och Javascript/QML, med ytterligare externa insticksprogram som stöder t.ex. PHP eller Python.

KDevelop C, C++ ve Javascript/QML için ayrıştırma arka uçlarına sahiptir, harici eklentilerle örn. PHP veya Python da desteklenir.

У KDevelop передбачено модулі обробки коду мовами C, C++ та Javascript/QML та додатки для розширення підтримки коду іншими мовами програмування, зокрема PHP і Python.

xxKDevelop has parser backends for C, C++ and Javascript/QML, with further external plugins supporting e.g. PHP or Python.xx

KDevelop 提供了 C、C++ 和 JavaScript/QML 的解析器后端,并通过插件提供 PHP 和 Python 等语言的支持。

https://kdevelop.org https://bugs.kde.org/enter_bug.cgi?format=guided&product=kdevelop https://docs.kde.org/index.php?application=kdevelop https://www.kde.org/community/donations/?app=kdevelop https://kde.org/images/screenshots/kdevelop.png KDE kdevelop org.kde.kdevelop.desktop kdevelop Development IDE kfunk@kde.org
diff --git a/plugins/android/kdevandroid.json b/plugins/android/kdevandroid.json index 47526d0680..2b6fe624f6 100644 --- a/plugins/android/kdevandroid.json +++ b/plugins/android/kdevandroid.json @@ -1,91 +1,85 @@ { "KPlugin": { "Authors": [ { "Email": "aleixpol@kde.org", "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Runtimes", "Description": "Exposes Android runtimes", "Description[ca@valencia]": "Exposa el temps d'execució de l'Android", "Description[ca]": "Exposa el temps d'execució de l'Android", "Description[de]": "Stellt Android-Laufzeitumgebungen bereit", - "Description[el]": "Εμφανίζει τις εκτελέσεις του Android", "Description[en_GB]": "Exposes Android runtimes", "Description[es]": "Expone bibliotecas en tiempo de ejecución de Android", "Description[et]": "Androidi käitusaegade näitamine", "Description[fr]": "Expose les exécutifs Android", "Description[gl]": "Expón execucións de Android.", "Description[it]": "Espone i runtime di Android", "Description[nl]": "Toont Android runtimes", "Description[pl]": "Udostępnia biblioteki uruchomieniowe Androida", - "Description[pt]": "Expõe as bibliotecas do Android", + "Description[pt]": "Expõe os ambientes de execução do Android", "Description[pt_BR]": "Expõe as bibliotecas do Android", "Description[sk]": "Vystavuje Android runtime", - "Description[sl]": "Izpostavi izvajalne knjižnice za Android", "Description[sv]": "Exponerar Android-körtidsprogram", "Description[tr]": "Android çalışma zamanlarını gösterir", "Description[uk]": "Надає доступ до середовищ виконання Android", "Description[x-test]": "xxExposes Android runtimesxx", "Description[zh_CN]": "暴露 Android 运行时", "Icon": "kdevelop", "Id": "kdevandroid", "License": "GPL", "Name": "Android Support", "Name[ca@valencia]": "Implementació de l'Android", "Name[ca]": "Implementació de l'Android", "Name[cs]": "Podpora Androidu", "Name[de]": "Unterstützung für Android", - "Name[el]": "Υποστήριξη android", "Name[en_GB]": "Android Support", "Name[es]": "Implementación de Android", - "Name[et]": "Androidi toetus", "Name[fr]": "Prise en charge d'Android", "Name[gl]": "Compatibilidade con Android.", "Name[it]": "Supporto per Android", "Name[nl]": "Ondersteuning voor Android", "Name[nn]": "Android-støtte", "Name[pl]": "Obsługa Androida", "Name[pt]": "Suporte para o Android", "Name[pt_BR]": "Suporte ao Android", "Name[sk]": "Podpora Androidu", - "Name[sl]": "Podpora za Android", "Name[sv]": "Android-stöd", "Name[tr]": "Android Desteği", "Name[uk]": "Підтримка Android", "Name[x-test]": "xxAndroid Supportxx", "Name[zh_CN]": "Android 支持", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/appwizard/kdevappwizard.json b/plugins/appwizard/kdevappwizard.json index d8df6b747d..502666de42 100644 --- a/plugins/appwizard/kdevappwizard.json +++ b/plugins/appwizard/kdevappwizard.json @@ -1,104 +1,90 @@ { "KPlugin": { "Authors": [ { "Name": "Alexander Dymo", "Name[ca@valencia]": "Alexander Dymo", "Name[ca]": "Alexander Dymo", "Name[cs]": "Alexander Dymo", "Name[de]": "Alexander Dymo", - "Name[el]": "Alexander Dymo", "Name[en_GB]": "Alexander Dymo", "Name[es]": "Alexander Dymo", "Name[et]": "Alexander Dymo", "Name[fr]": "Alexander Dymo", "Name[gl]": "Alexander Dymo", "Name[it]": "Alexander Dymo", "Name[nl]": "Alexander Dymo", "Name[nn]": "Alexander Dymo", "Name[pl]": "Alexander Dymo", "Name[pt]": "Alexander Dymo", "Name[pt_BR]": "Alexander Dymo", "Name[ru]": "Александр Дымо", "Name[sk]": "Alexander Dymo", "Name[sl]": "Alexander Dymo", "Name[sv]": "Alexander Dymo", "Name[tr]": "Alexander Dymo", "Name[uk]": "Олександр Димо", "Name[x-test]": "xxAlexander Dymoxx", "Name[zh_CN]": "Alexander Dymo" } ], "Category": "Core", "Description": "Application Wizard", - "Description[ar]": "مُرشد تطبيق", "Description[ca@valencia]": "Assistent d'aplicació", "Description[ca]": "Assistent d'aplicació", "Description[cs]": "Průvodce aplikací", "Description[de]": "Anwendungsassistent", - "Description[el]": "Οδηγός εφαρμογής", "Description[en_GB]": "Application Wizard", "Description[es]": "Asistente de aplicaciones", "Description[et]": "Rakenduse nõustaja", "Description[fr]": "Assistant application", "Description[gl]": "Asistente para aplicacións", "Description[it]": "Procedura guidata per applicazioni", "Description[nl]": "Programma-assistent", "Description[pl]": "Pomocnik programu", "Description[pt]": "Assistente de Aplicações", "Description[pt_BR]": "Assistente de aplicativo", "Description[sk]": "Sprievodca aplikáciou", "Description[sl]": "Čarovnik za programe", "Description[sv]": "Programguide", "Description[tr]": "Uygulama Sihirbazı", "Description[uk]": "Майстер створення програм", "Description[x-test]": "xxApplication Wizardxx", "Description[zh_CN]": "应用程序向导", - "Description[zh_TW]": "應用程式精靈", "Icon": "project-development-new-template", "Id": "kdevappwizard", "License": "GPL", "Name": "New Project Wizard", - "Name[ar]": "مُرشد مشروع جديد", - "Name[bg]": "Съветник за нов проект", "Name[ca@valencia]": "Assistent de projecte nou", "Name[ca]": "Assistent de projecte nou", "Name[cs]": "Průvodce novým projektem", - "Name[da]": "Guide til nyt projekt", "Name[de]": "Projekt-Assistent", - "Name[el]": "Οδηγός ρύθμισης νέου έργου", "Name[en_GB]": "New Project Wizard", "Name[es]": "Asistente de nuevo proyecto", - "Name[et]": "Uue projekti nõustaja", "Name[fr]": "Assistant de création de nouveau projet", "Name[gl]": "Asistente para proxectos novos", - "Name[hu]": "Új projekt varázsló", "Name[it]": "Procedura guidata nuovo progetto", - "Name[kk]": "Жаңа жоба шебері", "Name[nb]": "Veiviser for nytt prosjekt", - "Name[nds]": "Projekt-Hölper", "Name[nl]": "Nieuwe projectenassistent", "Name[pl]": "Pomocnik nowego projektu", "Name[pt]": "Assistente de Novos Projectos", "Name[pt_BR]": "Assistente de novo projeto", "Name[ru]": "Мастер создания проекта", "Name[sk]": "Sprievodca novým projektom", "Name[sl]": "Čarovnik za nov projekt", "Name[sv]": "Ny projektguide", "Name[tr]": "Yeni Proje Sihirbazı", - "Name[ug]": "يېڭى قۇرۇلۇش يېتەكچىسى", "Name[uk]": "Майстер створення проєкту", "Name[x-test]": "xxNew Project Wizardxx", "Name[zh_CN]": "新工程向导", - "Name[zh_TW]": "新增專案精靈", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.ITemplateProvider" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/appwizard/kdevappwizard.knsrc b/plugins/appwizard/kdevappwizard.knsrc index 0573f7369a..bc26b8062a 100644 --- a/plugins/appwizard/kdevappwizard.knsrc +++ b/plugins/appwizard/kdevappwizard.knsrc @@ -1,29 +1,28 @@ [KNewStuff3] Name=Application Templates (SDK) Name[ca]=Plantilles d'aplicació (SDK) Name[ca@valencia]=Plantilles d'aplicació (SDK) Name[cs]=Šablony aplikací (SDK) Name[de]=Anwendungsvorlagen (SDK) -Name[el]=Εφαρμογές templates (SDK) Name[en_GB]=Application Templates (SDK) Name[es]=Plantillas de aplicaciones (SDK) Name[et]=Rakendusemallid (SDK) Name[fr]=Modèle d'application (SDK) Name[gl]=Modelos de aplicación (SDK) Name[it]=Modelli di applicazione (SDK) Name[nl]=Toepassingsjablonen (SDK) Name[pl]=Szablony aplikacji (SDK) Name[pt]=Modelos de Aplicações (SDK) Name[pt_BR]=Modelos de aplicativos (SDK) Name[sk]=Aplikačné šablóny (SDK) Name[sl]=Predloge programov (SDK) Name[sv]=Programmallar (SDK) Name[tr]=Uygulama Şablonları (SDK) Name[uk]=Шаблони програм (SDK) Name[x-test]=xxApplication Templates (SDK)xx Name[zh_CN]=应用程序模板 (SDK) ProvidersUrl=https://download.kde.org/ocs/providers.xml Categories=KDE App Template TargetDir=kdevappwizard/templates Uncompress=never diff --git a/plugins/astyle/kdevastyle.json b/plugins/astyle/kdevastyle.json index cd3711d079..d5ffe2d0c4 100644 --- a/plugins/astyle/kdevastyle.json +++ b/plugins/astyle/kdevastyle.json @@ -1,94 +1,89 @@ { "KPlugin": { "Authors": [ { "Name": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[ca@valencia]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[ca]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[cs]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[de]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", - "Name[el]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[en_GB]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[es]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[et]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[fi]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[fr]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[gl]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[it]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[nl]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[nn]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[pl]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[pt]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[pt_BR]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[ru]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[sk]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[sl]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[sv]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[tr]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[uk]": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[x-test]": "xxCedric Pasteur, Matthias Hölzer-Klüpfelxx", "Name[zh_CN]": "Cedric Pasteur, Matthias Hölzer-Klüpfel" } ], "Category": "Utilities", "Description": "A plugin for formatting of sourcecode according to a specified set of rules", - "Description[bs]": "Dodatak za formatiranje izvornog koda prema određenom nizu pravila", "Description[ca@valencia]": "Un connector per a formatar el codi font d'acord a un conjunt especificat de regles", "Description[ca]": "Un connector per a formatar el codi font d'acord a un conjunt especificat de regles", "Description[de]": "Modul zum Formatieren von Quelltext nach bestimmten Vorschriften", - "Description[el]": "Ένα πρόσθετο για τη διαμόρφωση πηγαίου κώδικα σύμφωνα με ένα ορισμένο σύνολο κανόνων", "Description[en_GB]": "A plugin for formatting of sourcecode according to a specified set of rules", "Description[es]": "Un complemento para formatear código fuente según el conjunto de reglas indicado", - "Description[et]": "Plugin lähtekoodi vormindamiseks vastavalt määratud reeglitele", + "Description[et]": "Plugin lähtekoodi vormindamiseks vastavalt konkreetsetele reeglitele.", "Description[fi]": "Liitännäinen lähdekoodin muotoilemiseen määritellyn sääntöjoukon mukaan", "Description[fr]": "Un module pour mettre en forme le code source d'après un ensemble de règles spécifié", "Description[gl]": "Complemento para formatar código fonte segundo un conxunto de regras indicado.", "Description[it]": "Un'estensione per la formattazione del codice sorgente secondo uno specifico insieme di regole", "Description[nl]": "Een plugin om broncode te formatteren volgens een speciale set regels", "Description[pl]": "Formatuje kod źródłowy zgodnie z zasadami", "Description[pt]": "Um 'plugin' para formatar código-fonte de acordo com um dado conjunto de regras", "Description[pt_BR]": "Um plugin para formatar código-fonte de acordo com um conjunto de regras", "Description[sk]": "Modul pre formátovanie zdrojového kódu podľa špecifických pravidiel", "Description[sl]": "Vstavek za oblikovanje izvorne kode po določenih pravilih", "Description[sv]": "Ett insticksprogram för formatering av källkod enligt en angiven uppsättning regler", "Description[tr]": "Belli kurallara göre kaynak kodları biçimlendirmeye yarayan bir eklenti", "Description[uk]": "Додаток для форматування коду у відповідності до вказаного набору правил", "Description[x-test]": "xxA plugin for formatting of sourcecode according to a specified set of rulesxx", "Description[zh_CN]": "一个根据指定规则设定格式化源代码的插件", "Icon": "text-field", "Id": "kdevastyle", "License": "LGPL", "Name": "AStyle Formatter Backend", - "Name[bs]": "AStyle formater pozadine", "Name[ca@valencia]": "Dorsal del formatador AStyle", "Name[ca]": "Dorsal del formatador AStyle", "Name[de]": "AStyle-Formatierer", - "Name[el]": "ύστημα υποστήριξης διαμορφωτή astyle", "Name[en_GB]": "AStyle Formatter Backend", "Name[es]": "Motor de formateo AStyle", - "Name[et]": "AStyle'i vormindaja taustaprogramm", + "Name[et]": "AStyle'i vormindamise taustaprogramm", "Name[fi]": "AStyle-muotoilijataustaohjelma", "Name[fr]": "Moteur de mise en forme AStyle", "Name[gl]": "Infraestrutura de formatación de AStyle", "Name[it]": "Backend formattatore AStyle", "Name[nl]": "Backend voor AStyle formatteerprogramma", "Name[pl]": "Silnik formatowania AStyle", - "Name[pt]": "Infra-Estrutura de Formatação do Astyle", + "Name[pt]": "Infra-Estrutura de Formatação do AStyle", "Name[pt_BR]": "Formatador AStyle", "Name[sk]": "Backend formátovača AStyle", "Name[sl]": "Zaledje oblikovalnika AStyle", "Name[sv]": "Astyle-formateringsgränssnitt", "Name[tr]": "Astyle Biçimlendirici Arka Ucu", "Name[uk]": "Сервер форматування AStyle", "Name[x-test]": "xxAStyle Formatter Backendxx", "Name[zh_CN]": "AStyle 格式化器后端", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.ISourceFormatter" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/bazaar/kdevbazaar.json b/plugins/bazaar/kdevbazaar.json index b056dc5a42..9592d43827 100644 --- a/plugins/bazaar/kdevbazaar.json +++ b/plugins/bazaar/kdevbazaar.json @@ -1,105 +1,91 @@ { "KPlugin": { "Authors": [ { "Email": "d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com", "Name": "Maciej Poleski", "Name[ca@valencia]": "Maciej Poleski", "Name[ca]": "Maciej Poleski", "Name[cs]": "Maciej Poleski", "Name[de]": "Maciej Poleski", - "Name[el]": "Maciej Poleski", "Name[en_GB]": "Maciej Poleski", "Name[es]": "Maciej Poleski", "Name[et]": "Maciej Poleski", "Name[fr]": "Maciej Poleski", "Name[gl]": "Maciej Poleski", "Name[it]": "Maciej Poleski", "Name[nl]": "Maciej Poleski", "Name[nn]": "Maciej Poleski", "Name[pl]": "Maciej Poleski", "Name[pt]": "Maciej Poleski", "Name[pt_BR]": "Maciej Poleski", "Name[ru]": "Maciej Poleski", "Name[sk]": "Maciej Poleski", "Name[sl]": "Maciej Poleski", "Name[sv]": "Maciej Poleski", "Name[tr]": "Maciej Poleski", "Name[uk]": "Maciej Poleski", "Name[x-test]": "xxMaciej Poleskixx", "Name[zh_CN]": "Maciej Poleski" } ], "Category": "Version Control", "Description": "This plugin integrates Bazaar to KDevelop", - "Description[ar]": "تكامل هذه الملحقة مطوّرك ببازار", "Description[ca@valencia]": "Aquest connector integra el Bazaar al KDevelop", "Description[ca]": "Aquest connector integra el Bazaar al KDevelop", "Description[cs]": "Tento modul integruje podporu pro Bazaar v KDevelop", "Description[de]": "Dieses Modul integriert Bazaar in KDevelop", - "Description[el]": "Αυτό το πρόσθετο ενσωματώνει το Bazaar στο KDevelop", "Description[en_GB]": "This plugin integrates Bazaar to KDevelop", "Description[es]": "Este complemento integra Bazaar en KDevelop", "Description[et]": "See plugin lõimib Bazaari KDevelopiga", "Description[fr]": "Ce module intègre Bazaar dans KDevelop", "Description[gl]": "Este complemento integra Bazaar con KDevelop", "Description[it]": "Questa estensione integra Bazaar in KDevelop", "Description[nl]": "Deze plugin integreert Bazaar in KDevelop", "Description[pl]": "Wplata Bazaar w KDevelop", "Description[pt]": "Este 'plugin' integra o Bazaar no KDevelop", "Description[pt_BR]": "Esta extensão integra o Bazaar ao KDevelop", "Description[sk]": "Tento plugin integruje Bazaar do KDevelop.", "Description[sl]": "Ta vstavek v KDevelop vgradi Bazaar", "Description[sv]": "Insticksprogrammet integrerar Bazaar i KDevelop", "Description[tr]": "Bu eklenti Bazaar uygulamasını KDevelop ile bütünleştirir", "Description[uk]": "За допомогою цього додатка можна інтегрувати Bazaar до KDevelop", "Description[x-test]": "xxThis plugin integrates Bazaar to KDevelopxx", "Description[zh_CN]": "此插件将 Bazaar 整合到 KDevelop", - "Description[zh_TW]": "此外掛程式將 Bazaar 整合進 KDevelop 內", "Icon": "bazaar", "Id": "kdevbazaar", "License": "GPL", "Name": "Bazaar Support", - "Name[ar]": "دعم بازار", - "Name[bg]": "Поддръжка на Bazaar", "Name[ca@valencia]": "Implementació de Bazaar", "Name[ca]": "Implementació de Bazaar", "Name[cs]": "Podpora Bazaar", - "Name[da]": "Bazaar-understøttelse", "Name[de]": "Bazaar-Unterstützung", - "Name[el]": "Υποστήριξη Bazaar", "Name[en_GB]": "Bazaar Support", "Name[es]": "Implementación de Bazaar", - "Name[et]": "Bazaari toetus", "Name[fr]": "Prise en charge de Bazaar", - "Name[ga]": "Tacaíocht Bazaar", "Name[gl]": "Compatibilidade con Bazaar", "Name[it]": "Supporto per Bazaar", - "Name[kk]": "Bazaar қолдауы", "Name[nb]": "Bazaar-støtte", - "Name[nds]": "Bazaar-Ünnerstütten", "Name[nl]": "Ondersteuning van Bazaar", "Name[nn]": "DPMS-støtte", "Name[pl]": "Obsługa Bazaar", "Name[pt]": "Suporte para o Bazaar", "Name[pt_BR]": "Suporte a Bazaar", "Name[sk]": "Podpora Bazaar", "Name[sl]": "Podpora za Bazaar", "Name[sv]": "Bazaar-stöd", "Name[tr]": "Bazaar Desteği", - "Name[ug]": "Bazaar قوللىشى", "Name[uk]": "Підтримка Bazaar", "Name[x-test]": "xxBazaar Supportxx", "Name[zh_CN]": "Bazaar 支持", - "Name[zh_TW]": "Bazaar 支援", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "1.0" }, "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl", "org.kdevelop.IDistributedVersionControl" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/bazaar/org.kde.kdevelop_bzr.desktop b/plugins/bazaar/org.kde.kdevelop_bzr.desktop index 9d4c1fd3be..be33b1c672 100644 --- a/plugins/bazaar/org.kde.kdevelop_bzr.desktop +++ b/plugins/bazaar/org.kde.kdevelop_bzr.desktop @@ -1,69 +1,67 @@ [Desktop Entry] Type=Application Exec=kdevelop --ps --fetch %U MimeType=x-scheme-handler/bzr;x-scheme-handler/bzr+ssh;x-scheme-handler/lp; Icon=kdevelop Terminal=false Name=KDevelop (Fetch Bazaar Project) Name[ca]=KDevelop (obtenir un projecte de Bazaar) Name[ca@valencia]=KDevelop (obtindre un projecte de Bazaar) Name[de]=KDevelop (Bazaar-Projekt holen) -Name[el]=KDevelop (Λήψη έργου από Bazaar) Name[en_GB]=KDevelop (Fetch Bazaar Project) Name[es]=KDevelop (obtener proyecto de Bazaar) Name[et]=KDevelop (Bazaari projekti tõmbamine) Name[fr]=KDevelop (récupérer un projet Bazaar) Name[gl]=KDevelop (obter un proxecto de Bazaar) Name[it]=KDevelop (importa un progetto Bazaar) Name[nl]=KDevelop (Bazaar-project ophalen) Name[pl]=KDevelop (Pobierz projekt Bazaar) Name[pt]=KDevelop (Obter um Projecto do Bazaar) Name[pt_BR]=KDevelop (Obter projeto do Bazaar) -Name[sk]=KDevelop (Načítať Bazaar Projekt) -Name[sl]=KDevelop (pridobi projekt Bazaar) +Name[sk]=KDevelop (Stiahnuť Bazaar Projekt) Name[sv]=KDevelop (hämta Bazaar-projekt) Name[uk]=KDevelop (отримати проєкт Bazaar) Name[x-test]=xxKDevelop (Fetch Bazaar Project)xx Name[zh_CN]=KDevelop (获取 Bazaar 项目) GenericName=Integrated Development Environment GenericName[ar]=بيئة تطوير متكاملة GenericName[bs]=Integrisano razvojno okruženje GenericName[ca]=Entorn integrat de desenvolupament GenericName[ca@valencia]=Entorn integrat de desenvolupament GenericName[cs]=Integrované Vývojové Prostředí GenericName[da]=Integreret udviklingsmiljø (IDE) GenericName[de]=Integrierte Entwicklungsumgebung GenericName[el]=ολοκληρωμένο περιβάλλον ανάπτυξης GenericName[en_GB]=Integrated Development Environment GenericName[es]=Entorno de desarrollo integrado GenericName[et]=Integreeritud arenduskeskkond GenericName[fi]=Integroitu kehitysympäristö GenericName[fr]=Environnement de Développement Intégré GenericName[ga]=Timpeallacht Chomhtháite Fhorbartha GenericName[gl]=Entorno de desenvolvemento integrado GenericName[hne]=एकीकृत डेवलपमेंट वातावरन GenericName[hu]=Integrált fejlesztői környezet GenericName[it]=Ambiente di sviluppo integrato GenericName[ja]=統合開発環境 GenericName[kk]=Біріктірілген құрастыру ортасы GenericName[km]=Development Environment ដែល​បាន​រួមបញ្ចូល GenericName[lt]=Integruota programavimo aplinka GenericName[lv]=Integrēta izstrādes vide GenericName[nb]=Integrert utviklingsmiljø GenericName[nds]=Programmsmeed GenericName[nl]=Geïntegreerde ontwikkelomgeving GenericName[pl]=Zintegrowane środowisko programistyczne GenericName[pt]=Ambiente de Desenvolvimento Integrado GenericName[pt_BR]=Ambiente Integrado de Desenvolvimento GenericName[ru]=Интегрированная среда разработки GenericName[sk]=Integrované vývojové prostredie GenericName[sl]=Integrirano razvojno okolje GenericName[sv]=Integrerad utvecklingsmiljö GenericName[tr]=Bütünleşik Geliştirme Ortamı GenericName[ug]=يۈرۈشلەشتۈرۈلگەن ئىجادىيەت مۇھىتى GenericName[uk]=Комплексне середовище розробки GenericName[x-test]=xxIntegrated Development Environmentxx GenericName[zh_CN]=集成开发环境 GenericName[zh_TW]=整合開發環境 Categories=Qt;KDE;Development;IDE; NoDisplay=true diff --git a/plugins/clang/clangparsejob.cpp b/plugins/clang/clangparsejob.cpp index a0b0ca801b..5abadc833d 100644 --- a/plugins/clang/clangparsejob.cpp +++ b/plugins/clang/clangparsejob.cpp @@ -1,392 +1,392 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "clangparsejob.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clangsettings/clangsettingsmanager.h" #include "duchain/clanghelpers.h" #include "duchain/clangpch.h" #include "duchain/duchainutils.h" #include "duchain/parsesession.h" #include "duchain/clangindex.h" #include "duchain/clangparsingenvironmentfile.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "util/clangutils.h" #include "clangsupport.h" #include "duchain/documentfinderhelpers.h" #include #include #include #include #include using namespace KDevelop; namespace { QString findConfigFile(const QString& forFile, const QString& configFileName) { QDir dir = QFileInfo(forFile).dir(); while (dir.exists()) { const QFileInfo customIncludePaths(dir, configFileName); if (customIncludePaths.exists()) { return customIncludePaths.absoluteFilePath(); } if (!dir.cdUp()) { break; } } return {}; } Path::List readPathListFile(const QString& filepath) { if (filepath.isEmpty()) { return {}; } QFile f(filepath); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { return {}; } const QString text = QString::fromLocal8Bit(f.readAll()); const QStringList lines = text.split(QLatin1Char('\n'), QString::SkipEmptyParts); Path::List paths; paths.reserve(lines.length()); for (const auto& line : lines) { paths << Path(line); } return paths; } /** * File should contain the header to precompile and use while parsing * @returns the first path in the file */ Path userDefinedPchIncludeForFile(const QString& sourcefile) { const QString pchIncludeFilename = QStringLiteral(".kdev_pch_include"); const auto paths = readPathListFile(findConfigFile(sourcefile, pchIncludeFilename)); return paths.isEmpty() ? Path() : paths.first(); } ProjectFileItem* findProjectFileItem(const IndexedString& url, bool* hasBuildSystemInfo) { ProjectFileItem* file = nullptr; *hasBuildSystemInfo = false; const auto& projects = ICore::self()->projectController()->projects(); for (auto project : projects) { - auto files = project->filesForPath(url); + const auto files = project->filesForPath(url); if (files.isEmpty()) { continue; } file = files.last(); // A file might be defined in different targets. // Prefer file items defined inside a target with non-empty includes. for (auto f: files) { if (!dynamic_cast(f->parent())) { continue; } file = f; if (!IDefinesAndIncludesManager::manager()->includes(f, IDefinesAndIncludesManager::ProjectSpecific).isEmpty()) { break; } } } if (file && file->project()) { if (auto bsm = file->project()->buildSystemManager()) { *hasBuildSystemInfo = bsm->hasBuildInfo(file); } } return file; } ClangParsingEnvironmentFile* parsingEnvironmentFile(const TopDUContext* context) { return dynamic_cast(context->parsingEnvironmentFile().data()); } DocumentChangeTracker* trackerForUrl(const IndexedString& url) { return ICore::self()->languageController()->backgroundParser()->trackerForUrl(url); } } ClangParseJob::ClangParseJob(const IndexedString& url, ILanguageSupport* languageSupport) : ParseJob(url, languageSupport) { const auto tuUrl = clang()->index()->translationUnitForUrl(url); bool hasBuildSystemInfo; if (auto file = findProjectFileItem(tuUrl, &hasBuildSystemInfo)) { m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(file)); m_environment.addFrameworkDirectories(IDefinesAndIncludesManager::manager()->frameworkDirectories(file)); m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(file)); m_environment.setParserSettings(ClangSettingsManager::self()->parserSettings(file)); if (hasBuildSystemInfo) { // Assume the builder invokes the compiler in the build directory. m_environment.setWorkingDirectory(file->project()->buildSystemManager()->buildDirectory(file)); } } else { m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(tuUrl.str())); m_environment.addFrameworkDirectories(IDefinesAndIncludesManager::manager()->frameworkDirectories(tuUrl.str())); m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(tuUrl.str())); m_environment.setParserSettings(ClangSettingsManager::self()->parserSettings(tuUrl.str())); } const bool isSource = ClangHelpers::isSource(tuUrl.str()); m_environment.setQuality( isSource ? (hasBuildSystemInfo ? ClangParsingEnvironment::BuildSystem : ClangParsingEnvironment::Source) : ClangParsingEnvironment::Unknown ); m_environment.setTranslationUnitUrl(tuUrl); Path::List projectPaths; const auto& projects = ICore::self()->projectController()->projects(); projectPaths.reserve(projects.size()); for (auto project : projects) { projectPaths.append(project->path()); } m_environment.setProjectPaths(projectPaths); m_unsavedFiles = ClangUtils::unsavedFiles(); const auto documents = ICore::self()->documentController()->openDocuments(); for (auto* document : documents) { auto textDocument = document->textDocument(); if ( !textDocument ) { continue; } const IndexedString indexedUrl(textDocument->url()); if (indexedUrl == tuUrl) { m_tuDocumentIsUnsaved = true; } m_unsavedRevisions.insert(indexedUrl, ModificationRevision::revisionForFile(indexedUrl)); } if (auto tracker = trackerForUrl(url)) { tracker->reset(); } } ClangSupport* ClangParseJob::clang() const { return static_cast(languageSupport()); } void ClangParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread* /*thread*/) { QReadLocker parseLock(languageSupport()->parseLock()); if (abortRequested()) { return; } { const auto tuUrlStr = m_environment.translationUnitUrl().str(); if (!m_tuDocumentIsUnsaved && !QFile::exists(tuUrlStr)) { // maybe we requested a parse job some time ago but now the file // does not exist anymore. return early then clang()->index()->unpinTranslationUnitForUrl(document()); return; } m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includesInBackground(tuUrlStr)); m_environment.addFrameworkDirectories(IDefinesAndIncludesManager::manager()->frameworkDirectoriesInBackground(tuUrlStr)); m_environment.addDefines(IDefinesAndIncludesManager::manager()->definesInBackground(tuUrlStr)); m_environment.addParserArguments(IDefinesAndIncludesManager::manager()->parserArgumentsInBackground(tuUrlStr)); m_environment.setPchInclude(userDefinedPchIncludeForFile(tuUrlStr)); } if (abortRequested()) { return; } // NOTE: we must have all declarations, contexts and uses available for files that are opened in the editor // it is very hard to check this for all included files of this TU, and previously lead to problems // when we tried to skip function bodies as an optimization for files that where not open in the editor. // now, we always build everything, which is correct but a tad bit slower. we can try to optimize later. setMinimumFeatures(static_cast(minimumFeatures() | TopDUContext::AllDeclarationsContextsAndUses)); if (minimumFeatures() & AttachASTWithoutUpdating) { // The context doesn't need to be updated, but has no AST attached (restored from disk), // so attach AST to it, without updating DUChain ParseSession session(createSessionData()); DUChainWriteLocker lock; auto ctx = DUChainUtils::standardContextForUrl(document().toUrl()); if (!ctx) { clangDebug() << "Lost context while attaching AST"; return; } ctx->setAst(IAstContainer::Ptr(session.data())); if (minimumFeatures() & UpdateHighlighting) { lock.unlock(); languageSupport()->codeHighlighting()->highlightDUChain(ctx); } return; } { UrlParseLock urlLock(document()); if (abortRequested() || !isUpdateRequired(ParseSession::languageString())) { return; } } ParseSession session(ClangIntegration::DUChainUtils::findParseSessionData(document(), m_environment.translationUnitUrl())); if (abortRequested()) { return; } if (!session.data() || !session.reparse(m_unsavedFiles, m_environment)) { session.setData(createSessionData()); } if (!session.unit()) { // failed to parse file, unpin and don't try again clang()->index()->unpinTranslationUnitForUrl(document()); return; } if (!clang_getFile(session.unit(), document().byteArray().constData())) { // this parse job's document does not exist in the pinned translation unit // so we need to unpin and re-add this document // Ideally we'd reset m_environment and session, but this is much simpler // and shouldn't be a common case clang()->index()->unpinTranslationUnitForUrl(document()); if (!(minimumFeatures() & Rescheduled)) { auto features = static_cast(minimumFeatures() | Rescheduled); ICore::self()->languageController()->backgroundParser()->addDocument(document(), features, priority()); } return; } Imports imports = ClangHelpers::tuImports(session.unit()); IncludeFileContexts includedFiles; if (auto pch = clang()->index()->pch(m_environment)) { auto pchFile = pch->mapFile(session.unit()); includedFiles = pch->mapIncludes(session.unit()); includedFiles.insert(pchFile, pch->context()); auto tuFile = clang_getFile(session.unit(), m_environment.translationUnitUrl().byteArray().constData()); imports.insert(tuFile, { pchFile, CursorInRevision(0, 0) } ); } if (abortRequested()) { return; } auto context = ClangHelpers::buildDUChain(session.mainFile(), imports, session, minimumFeatures(), includedFiles, clang()->index(), [this] { return abortRequested(); }); setDuChain(context); if (abortRequested()) { return; } if (context) { if (minimumFeatures() & TopDUContext::AST) { DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } #ifdef QT_DEBUG DUChainReadLocker lock; auto file = parsingEnvironmentFile(context); Q_ASSERT(file); // verify that features and environment where properly set in ClangHelpers::buildDUChain Q_ASSERT(file->featuresSatisfied(TopDUContext::Features(minimumFeatures() & ~TopDUContext::ForceUpdateRecursive))); if (trackerForUrl(context->url())) { Q_ASSERT(file->featuresSatisfied(TopDUContext::AllDeclarationsContextsAndUses)); } #endif } for (const auto& context : qAsConst(includedFiles)) { if (!context) { continue; } { // prefer the editor modification revision, instead of the on-disk revision auto it = m_unsavedRevisions.find(context->url()); if (it != m_unsavedRevisions.end()) { DUChainWriteLocker lock; auto file = parsingEnvironmentFile(context); Q_ASSERT(file); file->setModificationRevision(it.value()); } } if (trackerForUrl(context->url())) { if (clang()->index()->translationUnitForUrl(context->url()) == m_environment.translationUnitUrl()) { // cache the parse session and the contained translation unit for this chain // this then allows us to quickly reparse the document if it is changed by // the user // otherwise no editor component is open for this document and we can dispose // the TU to save memory // share the session data with all contexts that are pinned to this TU DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } languageSupport()->codeHighlighting()->highlightDUChain(context); } } } ParseSessionData::Ptr ClangParseJob::createSessionData() const { return ParseSessionData::Ptr(new ParseSessionData(m_unsavedFiles, clang()->index(), m_environment, ParseSessionData::NoOption)); } const ParsingEnvironment* ClangParseJob::environment() const { return &m_environment; } diff --git a/plugins/clang/codecompletion/context.cpp b/plugins/clang/codecompletion/context.cpp index 8ef06c769e..5565940b12 100644 --- a/plugins/clang/codecompletion/context.cpp +++ b/plugins/clang/codecompletion/context.cpp @@ -1,1395 +1,1395 @@ /* * This file is part of KDevelop * Copyright 2014 Milian Wolff * Copyright 2015 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "context.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../util/clangdebug.h" #include "../util/clangtypes.h" #include "../util/clangutils.h" #include "../duchain/clangdiagnosticevaluator.h" #include "../duchain/parsesession.h" #include "../duchain/duchainutils.h" #include "../duchain/navigationwidget.h" #include "../clangsettings/clangsettingsmanager.h" #include #include #include #include #include using namespace KDevelop; namespace { /// Maximum return-type string length in completion items const int MAX_RETURN_TYPE_STRING_LENGTH = 20; /// Priority of code-completion results. NOTE: Keep in sync with Clang code base. enum CodeCompletionPriority { /// Priority for the next initialization in a constructor initializer list. CCP_NextInitializer = 7, /// Priority for an enumeration constant inside a switch whose condition is of the enumeration type. CCP_EnumInCase = 7, CCP_LocalDeclarationMatch = 8, CCP_DeclarationMatch = 12, CCP_LocalDeclarationSimiliar = 17, /// Priority for a send-to-super completion. CCP_SuperCompletion = 20, CCP_DeclarationSimiliar = 25, /// Priority for a declaration that is in the local scope. CCP_LocalDeclaration = 34, /// Priority for a member declaration found from the current method or member function. CCP_MemberDeclaration = 35, /// Priority for a language keyword (that isn't any of the other categories). CCP_Keyword = 40, /// Priority for a code pattern. CCP_CodePattern = 40, /// Priority for a non-type declaration. CCP_Declaration = 50, /// Priority for a type. CCP_Type = CCP_Declaration, /// Priority for a constant value (e.g., enumerator). CCP_Constant = 65, /// Priority for a preprocessor macro. CCP_Macro = 70, /// Priority for a nested-name-specifier. CCP_NestedNameSpecifier = 75, /// Priority for a result that isn't likely to be what the user wants, but is included for completeness. CCP_Unlikely = 80 }; /** * Common base class for Clang code completion items. */ template class CompletionItem : public Base { public: CompletionItem(const QString& display, const QString& prefix) : Base() , m_display(display) , m_prefix(prefix) , m_unimportant(false) { } ~CompletionItem() override = default; QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* /*model*/) const override { if (role == Qt::DisplayRole) { if (index.column() == CodeCompletionModel::Prefix) { return m_prefix; } else if (index.column() == CodeCompletionModel::Name) { return m_display; } } return {}; } void markAsUnimportant() { m_unimportant = true; } protected: QString m_display; QString m_prefix; bool m_unimportant; }; class OverrideItem : public CompletionItem { public: OverrideItem(const QString& nameAndParams, const QString& returnType) : CompletionItem( nameAndParams, i18n("Override %1", returnType) ) , m_returnType(returnType) { } QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override { if (role == Qt::DecorationRole) { if (index.column() == KTextEditor::CodeCompletionModel::Icon) { return QIcon::fromTheme(QStringLiteral("CTparents")); } } return CompletionItem::data(index, role, model); } void execute(KTextEditor::View* view, const KTextEditor::Range& word) override { QString replacement = m_returnType + QLatin1Char(' ') + m_display.replace(QRegularExpression(QStringLiteral("\\s*=\\s*0")), QString()); bool appendSpecifer = true; if (const auto* project = KDevelop::ICore::self()->projectController()->findProjectForUrl(view->document()->url())) { const auto arguments = KDevelop::IDefinesAndIncludesManager::manager()->parserArguments( project->filesForPath(IndexedString(view->document()->url().path())).first()); const auto match = QRegularExpression(QStringLiteral(R"(-std=c\+\+(\w+))")).match(arguments); appendSpecifer = match.hasMatch(); // assume non-modern if no standard is specified if (appendSpecifer) { const auto standard = match.capturedRef(1); appendSpecifer = (standard != QLatin1String("98") && standard != QLatin1String("03")); } } if (appendSpecifer) { replacement.append(QLatin1String(" override;")); } else { replacement.append(QLatin1Char(';')); } DocumentChange overrideChange(IndexedString(view->document()->url()), word, QString{}, replacement); overrideChange.m_ignoreOldText = true; DocumentChangeSet changes; changes.addChange(overrideChange); changes.applyAllChanges(); } private: QString m_returnType; }; /** * Specialized completion item class for items which are represented by a Declaration */ class DeclarationItem : public CompletionItem { public: DeclarationItem(Declaration* dec, const QString& display, const QString& prefix, const QString& replacement) : CompletionItem(display, prefix) , m_replacement(replacement) { m_declaration = dec; } QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override { if (role == CodeCompletionModel::MatchQuality && m_matchQuality) { return m_matchQuality; } auto ret = CompletionItem::data(index, role, model); if (ret.isValid()) { return ret; } return NormalDeclarationCompletionItem::data(index, role, model); } void execute(KTextEditor::View* view, const KTextEditor::Range& word) override { QString repl = m_replacement; DUChainReadLocker lock; if(!m_declaration){ return; } if(m_declaration->isFunctionDeclaration()) { const auto functionType = m_declaration->type(); // protect against buggy code that created the m_declaration, // to mark it as a function but not assign a function type if (!functionType) return; auto doc = view->document(); // Function pointer? bool funcptr = false; const auto line = doc->line(word.start().line()); auto pos = word.end().column() - 1; while ( pos > 0 && (line.at(pos).isLetterOrNumber() || line.at(pos) == QLatin1Char(':')) ) { pos--; if ( line.at(pos) == QLatin1Char('&') ) { funcptr = true; break; } } auto restEmpty = doc->characterAt(word.end() + KTextEditor::Cursor{0, 1}) == QChar(); bool didAddParentheses = false; if ( !funcptr && doc->characterAt(word.end()) != QLatin1Char('(') ) { repl += QLatin1String("()"); didAddParentheses = true; } view->document()->replaceText(word, repl); if (functionType->indexedArgumentsSize() && didAddParentheses) { view->setCursorPosition(word.start() + KTextEditor::Cursor(0, repl.size() - 1)); } auto returnTypeIntegral = functionType->returnType().cast(); if ( restEmpty && !funcptr && returnTypeIntegral && returnTypeIntegral->dataType() == IntegralType::TypeVoid ) { // function returns void and rest of line is empty -- nothing can be done with the result if (functionType->indexedArgumentsSize() ) { // we placed the cursor inside the () view->document()->insertText(view->cursorPosition() + KTextEditor::Cursor(0, 1), QStringLiteral(";")); } else { // we placed the cursor after the () view->document()->insertText(view->cursorPosition(), QStringLiteral(";")); view->setCursorPosition(view->cursorPosition() + KTextEditor::Cursor{0, 1}); } } } else { view->document()->replaceText(word, repl); } } bool createsExpandingWidget() const override { return true; } QWidget* createExpandingWidget(const CodeCompletionModel* /*model*/) const override { return new ClangNavigationWidget(m_declaration, AbstractNavigationWidget::EmbeddableWidget); } int matchQuality() const { return m_matchQuality; } ///Sets match quality from 0 to 10. 10 is the best fit. void setMatchQuality(int value) { m_matchQuality = value; } void setInheritanceDepth(int depth) { m_inheritanceDepth = depth; } int argumentHintDepth() const override { return m_depth; } void setArgumentHintDepth(int depth) { m_depth = depth; } protected: int m_matchQuality = 0; int m_depth = 0; QString m_replacement; }; class ImplementsItem : public DeclarationItem { public: static QString replacement(const FuncImplementInfo& info) { QString replacement = info.templatePrefix; if (!info.isDestructor && !info.isConstructor) { replacement += info.returnType + QLatin1Char(' '); } replacement += info.prototype + QLatin1String("\n{\n}\n"); return replacement; } explicit ImplementsItem(const FuncImplementInfo& item) : DeclarationItem(item.declaration.data(), item.prototype, i18n("Implement %1", item.isConstructor ? QStringLiteral("") : item.isDestructor ? QStringLiteral("") : item.returnType), replacement(item) ) { } QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override { if (index.column() == CodeCompletionModel::Arguments) { // our display string already contains the arguments return {}; } return DeclarationItem::data(index, role, model); } void execute(KTextEditor::View* view, const KTextEditor::Range& word) override { auto* const document = view->document(); DocumentChangeSet changes; KTextEditor::Cursor rangeStart = word.start(); // try and replace leading typed text that match the proposed implementation const QString leading = document->line(word.end().line()).left(word.end().column()); const QString leadingNoSpace = removeWhitespace(leading); if (!leadingNoSpace.isEmpty() && (removeWhitespace(m_display).startsWith(leadingNoSpace) || removeWhitespace(m_replacement).startsWith(leadingNoSpace))) { const int removeSize = leading.end() - std::find_if_not(leading.begin(), leading.end(), [](QChar c){ return c.isSpace(); }); rangeStart = {word.end().line(), word.end().column() - removeSize}; } DocumentChange change(IndexedString(view->document()->url()), KTextEditor::Range(rangeStart, word.end()), QString(), m_replacement); change.m_ignoreOldText = true; changes.addChange(change); changes.applyAllChanges(); // Place cursor after the opening brace // arbitrarily chose 4, as it would accomodate the template and return types on their own line const auto searchRange = KTextEditor::Range(rangeStart, rangeStart.line() + 4, 0); const auto results = view->document()->searchText(searchRange, QStringLiteral("{")); if (!results.isEmpty()) { view->setCursorPosition(results.first().end()); } } }; class ArgumentHintItem : public DeclarationItem { public: struct CurrentArgumentRange { int start; int end; }; ArgumentHintItem(Declaration* decl, const QString& prefix, const QString& name, const QString& arguments, const CurrentArgumentRange& range) : DeclarationItem(decl, name, prefix, {}) , m_range(range) , m_arguments(arguments) {} QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override { if (role == CodeCompletionModel::CustomHighlight && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) { QTextCharFormat boldFormat; boldFormat.setFontWeight(QFont::Bold); const QList highlighting { QVariant(m_range.start), QVariant(m_range.end), boldFormat, }; return highlighting; } if (role == CodeCompletionModel::HighlightingMethod && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) { return QVariant(CodeCompletionModel::CustomHighlighting); } if (index.column() == CodeCompletionModel::Arguments) { return m_arguments; } return DeclarationItem::data(index, role, model); } private: CurrentArgumentRange m_range; QString m_arguments; }; /** * A minimalistic completion item for macros and such */ class SimpleItem : public CompletionItem { public: SimpleItem(const QString& display, const QString& prefix, const QString& replacement, const QIcon& icon = QIcon()) : CompletionItem(display, prefix) , m_replacement(replacement) , m_icon(icon) { } void execute(KTextEditor::View* view, const KTextEditor::Range& word) override { view->document()->replaceText(word, m_replacement); } QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override { if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Icon) { return m_icon; } if (role == CodeCompletionModel::UnimportantItemRole) { return m_unimportant; } return CompletionItem::data(index, role, model); } private: QString m_replacement; QIcon m_icon; }; /** * Return true in case position @p position represents a cursor inside a comment */ bool isInsideComment(CXTranslationUnit unit, CXFile file, const KTextEditor::Cursor& position) { if (!position.isValid()) { return false; } // TODO: This may get very slow for a large TU, investigate if we can improve this function auto begin = clang_getLocation(unit, file, 1, 1); auto end = clang_getLocation(unit, file, position.line() + 1, position.column() + 1); CXSourceRange range = clang_getRange(begin, end); // tokenize the whole range from the start until 'position' // if we detect a comment token at this position, return true const ClangTokens tokens(unit, range); for (CXToken token : tokens) { CXTokenKind tokenKind = clang_getTokenKind(token); if (tokenKind != CXToken_Comment) { continue; } auto range = ClangRange(clang_getTokenExtent(unit, token)); if (range.toRange().contains(position)) { return true; } } return false; } QString& elideStringRight(QString& str, int length) { if (str.size() > length + 3) { return str.replace(length, str.size() - length, QStringLiteral("...")); } return str; } /** * @return Value suited for @ref CodeCompletionModel::MatchQuality in the range [0.0, 10.0] (the higher the better) * * See https://clang.llvm.org/doxygen/CodeCompleteConsumer_8h_source.html for list of priorities * They (currently) have a range from [-3, 80] (the lower, the better) */ int codeCompletionPriorityToMatchQuality(unsigned int completionPriority) { return 10u - qBound(0u, completionPriority, 80u) / 8; } int adjustPriorityForType(const AbstractType::Ptr& type, int completionPriority) { const auto modifier = 4; if (type) { const auto whichType = type->whichType(); if (whichType == AbstractType::TypePointer || whichType == AbstractType::TypeReference) { // Clang considers all pointers as similar, this is not what we want. completionPriority += modifier; } else if (whichType == AbstractType::TypeStructure) { // Clang considers all classes as similar too... completionPriority += modifier; } else if (whichType == AbstractType::TypeDelayed) { completionPriority += modifier; } else if (whichType == AbstractType::TypeAlias) { auto aliasedType = type.cast(); return adjustPriorityForType(aliasedType ? aliasedType->type() : AbstractType::Ptr(), completionPriority); } else if (whichType == AbstractType::TypeFunction) { auto functionType = type.cast(); return adjustPriorityForType(functionType ? functionType->returnType() : AbstractType::Ptr(), completionPriority); } } else { completionPriority += modifier; } return completionPriority; } /// Adjusts priority for the @p decl int adjustPriorityForDeclaration(Declaration* decl, unsigned int completionPriority) { if(completionPriority < CCP_LocalDeclarationSimiliar || completionPriority > CCP_SuperCompletion){ return completionPriority; } return adjustPriorityForType(decl->abstractType(), completionPriority); } /** * @return Whether the declaration represented by identifier @p identifier qualifies as completion result * * For example, we don't want to offer SomeClass::SomeClass as completion item to the user * (otherwise we'd end up generating code such as 's.SomeClass();') */ bool isValidCompletionIdentifier(const QualifiedIdentifier& identifier) { const int count = identifier.count(); if (identifier.count() < 2) { return true; } const Identifier scope = identifier.at(count-2); const Identifier id = identifier.last(); if (scope == id) { return false; // is constructor } const QString idString = id.toString(); if (idString.startsWith(QLatin1Char('~')) && scope.toString() == idString.midRef(1)) { return false; // is destructor } return true; } /** * @return Whether the declaration represented by identifier @p identifier qualifies as "special" completion result * * "Special" completion results are items that are likely not regularly used. * * Examples: * - 'SomeClass::operator=(const SomeClass&)' */ bool isValidSpecialCompletionIdentifier(const QualifiedIdentifier& identifier) { if (identifier.count() < 2) { return false; } const Identifier id = identifier.last(); const QString idString = id.toString(); if (idString.startsWith(QLatin1String("operator="))) { return true; // is assignment operator } return false; } Declaration* findDeclaration(const QualifiedIdentifier& qid, const DUContextPointer& ctx, const CursorInRevision& position, QSet& handled) { PersistentSymbolTable::Declarations decl = PersistentSymbolTable::self().declarations(qid); const auto top = ctx->topContext(); const auto& importedContexts = top->importedParentContexts(); for (auto it = decl.iterator(); it; ++it) { // if the context is not included, then this match is not correct for our consideration // this fixes issues where we used to include matches from files that did not have // anything to do with the current TU, e.g. the main from a different file or stuff like that // it also reduces the chance of us picking up a function of the same name from somewhere else // also, this makes sure the context has the correct language and we don't get confused by stuff // from other language plugins if (std::none_of(importedContexts.begin(), importedContexts.end(), [it] (const DUContext::Import& import) { return import.topContextIndex() == it->indexedTopContext().index(); })) { continue; } auto declaration = it->declaration(); if (!declaration) { // Mitigate problems such as: Cannot load a top-context from file "/home/kfunk/.cache/kdevduchain/kdevelop-{foo}/topcontexts/6085" // - the required language-support for handling ID 55 is probably not loaded qCWarning(KDEV_CLANG) << "Detected an invalid declaration for" << qid; continue; } if (declaration->kind() == Declaration::Instance && !declaration->isFunctionDeclaration()) { break; } if (!handled.contains(declaration)) { handled.insert(declaration); return declaration; } } const auto foundDeclarations = ctx->findDeclarations(qid, position); for (auto dec : foundDeclarations) { if (!handled.contains(dec)) { handled.insert(dec); return dec; } } return nullptr; } /// If any parent of this context is a class, the closest class declaration is returned, nullptr otherwise Declaration* classDeclarationForContext(const DUContextPointer& context, const CursorInRevision& position) { auto parent = context; while (parent) { if (parent->type() == DUContext::Class) { break; } if (auto owner = parent->owner()) { // Work-around for out-of-line methods. They have Helper context instead of Class context if (owner->context() && owner->context()->type() == DUContext::Helper) { auto qid = owner->qualifiedIdentifier(); qid.pop(); QSet tmp; auto decl = findDeclaration(qid, context, position, tmp); if (decl && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) { parent = decl->internalContext(); break; } } } parent = parent->parentContext(); } return parent ? parent->owner() : nullptr; } class LookAheadItemMatcher { public: explicit LookAheadItemMatcher(const TopDUContextPointer& ctx) : m_topContext(ctx) , m_enabled(ClangSettingsManager::self()->codeCompletionSettings().lookAhead) {} /// Adds all local declarations for @p declaration into possible look-ahead items. void addDeclarations(Declaration* declaration) { if (!m_enabled) { return; } if (declaration->kind() != Declaration::Instance) { return; } auto type = typeForDeclaration(declaration); auto identifiedType = dynamic_cast(type.data()); if (!identifiedType) { return; } addDeclarationsForType(identifiedType, declaration); } /// Add type for matching. This type'll be used for filtering look-ahead items /// Only items with @p type will be returned through @sa matchedItems void addMatchedType(const IndexedType& type) { if (type.isValid()) { matchedTypes.insert(type); } } /// @return look-ahead items that math given types. @sa addMatchedType QList matchedItems() { QList lookAheadItems; for (const auto& pair: qAsConst(possibleLookAheadDeclarations)) { auto decl = pair.first; if (matchedTypes.contains(decl->indexedType())) { auto parent = pair.second; const QLatin1String access = (parent->abstractType()->whichType() == AbstractType::TypePointer) ? QLatin1String("->") : QLatin1String("."); const QString text = parent->identifier().toString() + access + decl->identifier().toString(); auto item = new DeclarationItem(decl, text, {}, text); item->setMatchQuality(8); lookAheadItems.append(CompletionTreeItemPointer(item)); } } return lookAheadItems; } private: AbstractType::Ptr typeForDeclaration(const Declaration* decl) { return TypeUtils::targetType(decl->abstractType(), m_topContext.data()); } void addDeclarationsForType(const IdentifiedType* identifiedType, Declaration* declaration) { if (auto typeDecl = identifiedType->declaration(m_topContext.data())) { if (dynamic_cast(typeDecl->logicalDeclaration(m_topContext.data()))) { if (!typeDecl->internalContext()) { return; } const auto& localDeclarations = typeDecl->internalContext()->localDeclarations(); for (auto localDecl : localDeclarations) { if(localDecl->identifier().isEmpty()){ continue; } if(auto classMember = dynamic_cast(localDecl)){ // TODO: Also add protected/private members if completion is inside this class context. if(classMember->accessPolicy() != Declaration::Public){ continue; } } if (!localDecl->abstractType()) { continue; } if (localDecl->abstractType()->whichType() == AbstractType::TypeIntegral) { if (auto integralType = declaration->abstractType().cast()) { if (integralType->dataType() == IntegralType::TypeVoid) { continue; } } } possibleLookAheadDeclarations.insert({localDecl, declaration}); } } } } // Declaration and it's context using DeclarationContext = QPair; /// Types of declarations that look-ahead completion items can have QSet matchedTypes; // List of declarations that can be added to the Look Ahead group // Second declaration represents context QSet possibleLookAheadDeclarations; TopDUContextPointer m_topContext; bool m_enabled; }; struct MemberAccessReplacer : public QObject { Q_OBJECT public: enum Type { None, DotToArrow, ArrowToDot }; public Q_SLOTS: void replaceCurrentAccess(MemberAccessReplacer::Type type) { if (auto document = ICore::self()->documentController()->activeDocument()) { if (auto textDocument = document->textDocument()) { auto activeView = document->activeTextView(); if (!activeView) { return; } auto cursor = activeView->cursorPosition(); QString oldAccess, newAccess; if (type == ArrowToDot) { oldAccess = QStringLiteral("->"); newAccess = QStringLiteral("."); } else { oldAccess = QStringLiteral("."); newAccess = QStringLiteral("->"); } auto oldRange = KTextEditor::Range(cursor - KTextEditor::Cursor(0, oldAccess.length()), cursor); // This code needed for testReplaceMemberAccess test // Maybe we should do a similar thing for '->' to '.' direction, but this is not so important while (textDocument->text(oldRange) == QLatin1String(" ") && oldRange.start().column() >= 0) { oldRange = KTextEditor::Range({oldRange.start().line(), oldRange.start().column() - 1}, {oldRange.end().line(), oldRange.end().column() - 1}); } if (oldRange.start().column() >= 0 && textDocument->text(oldRange) == oldAccess) { textDocument->replaceText(oldRange, newAccess); } } } } }; static MemberAccessReplacer s_memberAccessReplacer; } Q_DECLARE_METATYPE(MemberAccessReplacer::Type) ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context, const ParseSessionData::Ptr& sessionData, const QUrl& url, const KTextEditor::Cursor& position, const QString& text, const QString& followingText ) : CodeCompletionContext(context, text + followingText, CursorInRevision::castFromSimpleCursor(position), 0) , m_results(nullptr, clang_disposeCodeCompleteResults) , m_parseSessionData(sessionData) { qRegisterMetaType(); const QByteArray file = url.toLocalFile().toUtf8(); ParseSession session(m_parseSessionData); QVector otherUnsavedFiles; { ForegroundLock lock; otherUnsavedFiles = ClangUtils::unsavedFiles(); } QVector allUnsaved; { const unsigned int completeOptions = clang_defaultCodeCompleteOptions(); CXUnsavedFile unsaved; unsaved.Filename = file.constData(); const QByteArray content = m_text.toUtf8(); unsaved.Contents = content.constData(); unsaved.Length = content.size(); allUnsaved.reserve(otherUnsavedFiles.size() + 1); for (const auto& f : qAsConst(otherUnsavedFiles)) { allUnsaved.append(f.toClangApi()); } allUnsaved.append(unsaved); m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(), position.line() + 1, position.column() + 1, allUnsaved.data(), allUnsaved.size(), completeOptions)); if (!m_results) { qCWarning(KDEV_CLANG) << "Something went wrong during 'clang_codeCompleteAt' for file" << file; return; } auto numDiagnostics = clang_codeCompleteGetNumDiagnostics(m_results.get()); for (uint i = 0; i < numDiagnostics; i++) { auto diagnostic = clang_codeCompleteGetDiagnostic(m_results.get(), i); auto diagnosticType = ClangDiagnosticEvaluator::diagnosticType(diagnostic); clang_disposeDiagnostic(diagnostic); if (diagnosticType == ClangDiagnosticEvaluator::ReplaceWithArrowProblem || diagnosticType == ClangDiagnosticEvaluator::ReplaceWithDotProblem) { MemberAccessReplacer::Type replacementType; if (diagnosticType == ClangDiagnosticEvaluator::ReplaceWithDotProblem) { replacementType = MemberAccessReplacer::ArrowToDot; } else { replacementType = MemberAccessReplacer::DotToArrow; } QMetaObject::invokeMethod(&s_memberAccessReplacer, "replaceCurrentAccess", Qt::QueuedConnection, Q_ARG(MemberAccessReplacer::Type, replacementType)); m_valid = false; return; } } auto addMacros = ClangSettingsManager::self()->codeCompletionSettings().macros; if (!addMacros) { m_filters |= NoMacros; } } if (!m_results->NumResults) { const auto trimmedText = text.trimmed(); if (trimmedText.endsWith(QLatin1Char('.'))) { // TODO: This shouldn't be needed if Clang provided diagnostic. // But it doesn't always do it, so let's try to manually determine whether '.' is used instead of '->' m_text = trimmedText.leftRef(trimmedText.size() - 1) + QLatin1String("->"); CXUnsavedFile unsaved; unsaved.Filename = file.constData(); const QByteArray content = m_text.toUtf8(); unsaved.Contents = content.constData(); unsaved.Length = content.size(); allUnsaved[allUnsaved.size() - 1] = unsaved; m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(), position.line() + 1, position.column() + 1 + 1, allUnsaved.data(), allUnsaved.size(), clang_defaultCodeCompleteOptions())); if (m_results && m_results->NumResults) { QMetaObject::invokeMethod(&s_memberAccessReplacer, "replaceCurrentAccess", Qt::QueuedConnection, Q_ARG(MemberAccessReplacer::Type, MemberAccessReplacer::DotToArrow)); } m_valid = false; return; } } // check 'isValidPosition' after parsing the new content auto clangFile = session.file(file); if (!isValidPosition(session.unit(), clangFile)) { m_valid = false; return; } m_completionHelper.computeCompletions(session, clangFile, position); } ClangCodeCompletionContext::~ClangCodeCompletionContext() { } bool ClangCodeCompletionContext::isValidPosition(CXTranslationUnit unit, CXFile file) const { if (isInsideComment(unit, file, m_position.castToSimpleCursor())) { clangDebug() << "Invalid completion context: Inside comment"; return false; } return true; } QList ClangCodeCompletionContext::completionItems(bool& abort, bool /*fullCompletion*/) { if (!m_valid || !m_duContext || !m_results) { return {}; } const auto ctx = DUContextPointer(m_duContext->findContextAt(m_position)); /// Normal completion items, such as 'void Foo::foo()' QList items; /// Stuff like 'Foo& Foo::operator=(const Foo&)', etc. Not regularly used by our users. QList specialItems; /// Macros from the current context QList macros; /// Builtins reported by Clang QList builtin; // two sets of handled declarations to prevent duplicates and make sure we show // all available overloads QSet handled; // this is only used for the CXCursor_OverloadCandidate completion items QSet overloadsHandled; LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext())); // If ctx is/inside the Class context, this represents that context. const auto currentClassContext = classDeclarationForContext(ctx, m_position); // HACK: try to build a fallback parent ID from the USR // otherwise we won't identify typedefed anon structs correctly :( auto parentFromUSR = [this]() -> QString { const auto containerUSR = ClangString(clang_codeCompleteGetContainerUSR(m_results.get())).toString(); const auto lastAt = containerUSR.lastIndexOf(QLatin1Char('@')); if (lastAt <= 0 || containerUSR[lastAt - 1] != QLatin1Char('A')) // we use this hack only for _A_non stuff return {}; return containerUSR.mid(lastAt + 1); }; const auto fallbackParentFromUSR = parentFromUSR(); clangDebug() << "Clang found" << m_results->NumResults << "completion results"; for (uint i = 0; i < m_results->NumResults; ++i) { if (abort) { return {}; } auto result = m_results->Results[i]; #if CINDEX_VERSION_MINOR >= 30 const bool isOverloadCandidate = result.CursorKind == CXCursor_OverloadCandidate; #else const bool isOverloadCandidate = false; #endif const auto availability = clang_getCompletionAvailability(result.CompletionString); if (availability == CXAvailability_NotAvailable) { continue; } const bool isMacroDefinition = result.CursorKind == CXCursor_MacroDefinition; if (isMacroDefinition && m_filters & NoMacros) { continue; } const bool isBuiltin = (result.CursorKind == CXCursor_NotImplemented); if (isBuiltin && m_filters & NoBuiltins) { continue; } const bool isDeclaration = !isMacroDefinition && !isBuiltin; if (isDeclaration && m_filters & NoDeclarations) { continue; } if (availability == CXAvailability_NotAccessible && (!isDeclaration || !currentClassContext)) { continue; } // the string that would be needed to type, usually the identifier of something. Also we use it as name for code completion declaration items. QString typed; // the return type of a function e.g. QString resultType; // the replacement text when an item gets executed QString replacement; QString arguments; ArgumentHintItem::CurrentArgumentRange argumentRange; //BEGIN function signature parsing // nesting depth of parentheses int parenDepth = 0; enum FunctionSignatureState { // not yet inside the function signature Before, // any token is part of the function signature now Inside, // finished parsing the function signature After }; // current state FunctionSignatureState signatureState = Before; //END function signature parsing std::function processChunks = [&] (CXCompletionString completionString) { const uint chunks = clang_getNumCompletionChunks(completionString); for (uint j = 0; j < chunks; ++j) { const auto kind = clang_getCompletionChunkKind(completionString, j); if (kind == CXCompletionChunk_Optional) { completionString = clang_getCompletionChunkCompletionString(completionString, j); if (completionString) { processChunks(completionString); } continue; } // We don't need function signature for declaration items, we can get it directly from the declaration. Also adding the function signature to the "display" would break the "Detailed completion" option. if (isDeclaration && !typed.isEmpty()) { // TODO: When parent context for CXCursor_OverloadCandidate is fixed remove this check if (!isOverloadCandidate) { break; } } const QString string = ClangString(clang_getCompletionChunkText(completionString, j)).toString(); switch (kind) { case CXCompletionChunk_TypedText: typed = string; replacement += string; break; case CXCompletionChunk_ResultType: resultType = string; continue; case CXCompletionChunk_Placeholder: if (signatureState == Inside) { arguments += string; } continue; case CXCompletionChunk_LeftParen: if (signatureState == Before && !parenDepth) { signatureState = Inside; } parenDepth++; break; case CXCompletionChunk_RightParen: --parenDepth; if (signatureState == Inside && !parenDepth) { arguments += QLatin1Char(')'); signatureState = After; } break; case CXCompletionChunk_Text: if (isOverloadCandidate) { typed += string; } else if (result.CursorKind == CXCursor_EnumConstantDecl) { replacement += string; } else if (result.CursorKind == CXCursor_EnumConstantDecl) { replacement += string; } break; case CXCompletionChunk_CurrentParameter: argumentRange.start = arguments.size(); argumentRange.end = string.size(); break; default: break; } if (signatureState == Inside) { arguments += string; } } }; processChunks(result.CompletionString); // we have our own implementation of an override helper // TODO: use the clang-provided one, if available if (typed.endsWith(QLatin1String(" override"))) continue; // TODO: No closing paren if default parameters present if (isOverloadCandidate && !arguments.endsWith(QLatin1Char(')'))) { arguments += QLatin1Char(')'); } // ellide text to the right for overly long result types (templates especially) elideStringRight(resultType, MAX_RETURN_TYPE_STRING_LENGTH); static const auto noIcon = QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevelop/pics/namespace.png"))); if (isDeclaration) { const Identifier id(typed); QualifiedIdentifier qid; auto parent = ClangString(clang_getCompletionParent(result.CompletionString, nullptr)).toString(); if (parent.isEmpty() && !fallbackParentFromUSR.isEmpty()) { parent = fallbackParentFromUSR; } if (!parent.isEmpty()) { qid = QualifiedIdentifier(parent); } qid.push(id); if (!isValidCompletionIdentifier(qid)) { continue; } if (isOverloadCandidate && resultType.isEmpty() && parent.isEmpty()) { // workaround: find constructor calls for non-namespaced classes // TODO: return the namespaced class as parent in libclang qid.push(id); } auto found = findDeclaration(qid, ctx, m_position, isOverloadCandidate ? overloadsHandled : handled); CompletionTreeItemPointer item; if (found) { // TODO: Bug in Clang: protected members from base classes not accessible in derived classes. if (availability == CXAvailability_NotAccessible) { if (auto cl = dynamic_cast(found)) { if (cl->accessPolicy() != Declaration::Protected) { continue; } auto declarationClassContext = classDeclarationForContext(DUContextPointer(found->context()), m_position); uint steps = 10; auto inheriters = DUChainUtils::inheriters(declarationClassContext, steps); if(!inheriters.contains(currentClassContext)){ continue; } } else { continue; } } DeclarationItem* declarationItem = nullptr; if (isOverloadCandidate) { declarationItem = new ArgumentHintItem(found, resultType, typed, arguments, argumentRange); declarationItem->setArgumentHintDepth(1); } else { declarationItem = new DeclarationItem(found, typed, resultType, replacement); } const unsigned int completionPriority = adjustPriorityForDeclaration(found, clang_getCompletionPriority(result.CompletionString)); const bool bestMatch = completionPriority <= CCP_SuperCompletion; //don't set best match property for internal identifiers, also prefer declarations from current file const auto isInternal = found->indexedIdentifier().identifier().toString().startsWith(QLatin1String("__")); if (bestMatch && !isInternal) { const int matchQuality = codeCompletionPriorityToMatchQuality(completionPriority); declarationItem->setMatchQuality(matchQuality); // TODO: LibClang missing API to determine expected code completion type. if (auto functionType = found->type()) { lookAheadMatcher.addMatchedType(IndexedType(functionType->returnType())); } lookAheadMatcher.addMatchedType(found->indexedType()); } else { declarationItem->setInheritanceDepth(completionPriority); lookAheadMatcher.addDeclarations(found); } if ( isInternal ) { declarationItem->markAsUnimportant(); } item = declarationItem; } else { if (isOverloadCandidate) { // TODO: No parent context for CXCursor_OverloadCandidate items, hence qid is broken -> no declaration found auto ahi = new ArgumentHintItem({}, resultType, typed, arguments, argumentRange); ahi->setArgumentHintDepth(1); item = ahi; } else { // still, let's trust that Clang found something useful and put it into the completion result list clangDebug() << "Could not find declaration for" << qid; auto instance = new SimpleItem(typed + arguments, resultType, replacement, noIcon); instance->markAsUnimportant(); item = CompletionTreeItemPointer(instance); } } if (isValidSpecialCompletionIdentifier(qid)) { // If it's a special completion identifier e.g. "operator=(const&)" and we don't have a declaration for it, don't add it into completion list, as this item is completely useless and pollutes the test case. // This happens e.g. for "class A{}; a.|". At | we have "operator=(const A&)" as a special completion identifier without a declaration. if(item->declaration()){ specialItems.append(item); } } else { items.append(item); } continue; } if (result.CursorKind == CXCursor_MacroDefinition) { // TODO: grouping of macros and built-in stuff const auto text = QString(typed + arguments); auto instance = new SimpleItem(text, resultType, replacement, noIcon); auto item = CompletionTreeItemPointer(instance); if ( text.startsWith(QLatin1Char('_')) ) { instance->markAsUnimportant(); } macros.append(item); } else if (result.CursorKind == CXCursor_NotImplemented) { auto instance = new SimpleItem(typed, resultType, replacement, noIcon); auto item = CompletionTreeItemPointer(instance); builtin.append(item); } } if (abort) { return {}; } addImplementationHelperItems(); addOverwritableItems(); eventuallyAddGroup(i18n("Special"), 700, specialItems); eventuallyAddGroup(i18n("Look-ahead Matches"), 800, lookAheadMatcher.matchedItems()); eventuallyAddGroup(i18n("Builtin"), 900, builtin); eventuallyAddGroup(i18n("Macros"), 1000, macros); return items; } void ClangCodeCompletionContext::eventuallyAddGroup(const QString& name, int priority, const QList& items) { if (items.isEmpty()) { return; } auto* node = new CompletionCustomGroupNode(name, priority); node->appendChildren(items); m_ungrouped << CompletionTreeElementPointer(node); } void ClangCodeCompletionContext::addOverwritableItems() { - auto overrideList = m_completionHelper.overrides(); + const auto overrideList = m_completionHelper.overrides(); if (overrideList.isEmpty()) { return; } QList overrides; QList overridesAbstract; for (const auto& info : overrideList) { QStringList params; params.reserve(info.params.size()); for (const auto& param : info.params) { params << param.type + QLatin1Char(' ') + param.id; } QString nameAndParams = info.name + QLatin1Char('(') + params.join(QLatin1String(", ")) + QLatin1Char(')'); if(info.isConst) nameAndParams = nameAndParams + QLatin1String(" const"); if(info.isPureVirtual) nameAndParams = nameAndParams + QLatin1String(" = 0"); auto item = CompletionTreeItemPointer(new OverrideItem(nameAndParams, info.returnType)); if (info.isPureVirtual) overridesAbstract << item; else overrides << item; } eventuallyAddGroup(i18n("Abstract Override"), 0, overridesAbstract); eventuallyAddGroup(i18n("Virtual Override"), 0, overrides); } void ClangCodeCompletionContext::addImplementationHelperItems() { const auto implementsList = m_completionHelper.implements(); if (implementsList.isEmpty()) { return; } QList implements; implements.reserve(implementsList.size()); for (const auto& info : implementsList) { implements << CompletionTreeItemPointer(new ImplementsItem(info)); } eventuallyAddGroup(i18n("Implement Function"), 0, implements); } QList ClangCodeCompletionContext::ungroupedElements() { return m_ungrouped; } ClangCodeCompletionContext::ContextFilters ClangCodeCompletionContext::filters() const { return m_filters; } void ClangCodeCompletionContext::setFilters(const ClangCodeCompletionContext::ContextFilters& filters) { m_filters = filters; } #include "context.moc" diff --git a/plugins/clang/duchain/documentfinderhelpers.cpp b/plugins/clang/duchain/documentfinderhelpers.cpp index 34e58f3faf..dbb50ab0ce 100644 --- a/plugins/clang/duchain/documentfinderhelpers.cpp +++ b/plugins/clang/duchain/documentfinderhelpers.cpp @@ -1,284 +1,284 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "documentfinderhelpers.h" #include "duchain/clanghelpers.h" #include #include #include #include #include #include using namespace KDevelop; namespace { enum FileType { Unknown, ///< Doesn't belong to C++ Header, ///< Is a header file Source ///< Is a C(++) file }; class PotentialBuddyCollector : public DUChainUtils::DUChainItemFilter { public: enum BuddyMode { Header, Source }; explicit PotentialBuddyCollector(BuddyMode mode) : mode(mode) {} bool accept(Declaration* decl) override { if (decl->range().isEmpty()) return false; if (mode == Header && decl->isFunctionDeclaration()) { // Search for definitions of our declarations FunctionDefinition* def = FunctionDefinition::definition(decl); if (def) { vote(def->url().toUrl()); } return true; } else if (mode == Source && decl->isFunctionDeclaration()) { auto* fdef = dynamic_cast(decl); if (fdef) { Declaration* fdecl = fdef->declaration(); if (fdecl) { vote(fdecl->url().toUrl()); } } return true; } else { return false; } } bool accept(DUContext* ctx) override { if (ctx->type() == DUContext::Class || ctx->type() == DUContext::Namespace || ctx->type() == DUContext::Global || ctx->type() == DUContext::Other || ctx->type() == DUContext::Helper ) { return true; } else { return false; } } QUrl bestBuddy() const { QUrl ret; int bestCount = 0; for (auto it = m_buddyFiles.begin(); it != m_buddyFiles.end(); ++it) { if(it.value() > bestCount) { bestCount = it.value(); ret = it.key(); } } return ret; } private: BuddyMode mode; QHash m_buddyFiles; void vote(const QUrl& url) { m_buddyFiles[url]++; } }; /** * Tries to find a buddy file to the given file by looking at the DUChain. * * The project might keep source files separate from headers. To cover * this situation, we examine DUChain for the most probable buddy file. * This of course only works if we have parsed the buddy file, but it is * better than nothing. * * @param url url of the source/header file to find a buddy for * @param type type of the file @p url * * @returns QUrl of the most probable buddy file, or an empty url **/ QUrl duchainBuddyFile(const QUrl& url, FileType type) { DUChainReadLocker lock; auto ctx = DUChainUtils::standardContextForUrl(url); if (ctx) { PotentialBuddyCollector collector( type == Header ? PotentialBuddyCollector::Header : PotentialBuddyCollector::Source ); DUChainUtils::collectItems(ctx, collector); return collector.bestBuddy(); } return QUrl(); } /** * Generates the base path (without extension) and the file type * for the specified url. * * @returns pair of base path and file type which has been found for @p url. */ QPair basePathAndTypeForUrl(const QUrl &url) { QString path = url.toLocalFile(); int idxSlash = path.lastIndexOf(QLatin1Char('/')); int idxDot = path.lastIndexOf(QLatin1Char('.')); FileType fileType = Unknown; QString basePath; if (idxSlash >= 0 && idxDot >= 0 && idxDot > idxSlash) { basePath = path.left(idxDot); if (idxDot + 1 < path.length()) { QString extension = path.mid(idxDot + 1); if (ClangHelpers::isHeader(extension)) { fileType = Header; } else if (ClangHelpers::isSource(extension)) { fileType = Source; } } } else { basePath = path; } return qMakePair(basePath, fileType); } } namespace DocumentFinderHelpers { QStringList mimeTypesList() { static const QStringList mimeTypes = { QStringLiteral("text/x-chdr"), QStringLiteral("text/x-c++hdr"), QStringLiteral("text/vnd.nvidia.cuda.chdr"), QStringLiteral("text/x-csrc"), QStringLiteral("text/x-c++src"), QStringLiteral("text/vnd.nvidia.cuda.csrc"), QStringLiteral("text/x-objcsrc") }; return mimeTypes; } bool areBuddies(const QUrl &url1, const QUrl& url2) { auto type1 = basePathAndTypeForUrl(url1); auto type2 = basePathAndTypeForUrl(url2); QUrl headerPath; QUrl sourcePath; // Check that one file is a header, the other one is source if (type1.second == Header && type2.second == Source) { headerPath = url1; sourcePath = url2; } else if (type1.second == Source && type2.second == Header) { headerPath = url2; sourcePath = url1; } else { // Some other file constellation return false; } // The simplest directory layout is with header + source in one directory. // So check that first. if (type1.first == type2.first) { return true; } // Also check if the DUChain thinks this is likely if (duchainBuddyFile(sourcePath, Source) == headerPath) { return true; } return false; } bool buddyOrder(const QUrl &url1, const QUrl& url2) { auto type1 = basePathAndTypeForUrl(url1); auto type2 = basePathAndTypeForUrl(url2); // Precondition is that the two URLs are buddies, so don't check it return(type1.second == Header && type2.second == Source); } QVector potentialBuddies(const QUrl& url, bool checkDUChain) { auto type = basePathAndTypeForUrl(url); // Don't do anything for types we don't know if (type.second == Unknown) { return {}; } // Depending on the buddy's file type we either generate source extensions (for headers) // or header extensions (for sources) const auto& extensions = ( type.second == Header ? ClangHelpers::sourceExtensions() : ClangHelpers::headerExtensions() ); QVector< QUrl > buddies; buddies.reserve(extensions.size()); for(const QString& extension : extensions) { if (!extension.contains(QLatin1Char('.'))) { buddies.append(QUrl::fromLocalFile(type.first + QLatin1Char('.') + extension)); } else { buddies.append(QUrl::fromLocalFile(type.first + extension)); } } if (checkDUChain) { // Also ask DUChain for a guess QUrl bestBuddy = duchainBuddyFile(url, type.second); if (!buddies.contains(bestBuddy)) { buddies.append(bestBuddy); } } return buddies; } QString sourceForHeader(const QString& headerPath) { if (!ClangHelpers::isHeader(headerPath)) { return {}; } QString targetUrl; - auto buddies = DocumentFinderHelpers::potentialBuddies(QUrl::fromLocalFile(headerPath)); + const auto buddies = DocumentFinderHelpers::potentialBuddies(QUrl::fromLocalFile(headerPath)); for (const auto& buddy : buddies) { const auto local = buddy.toLocalFile(); if (QFileInfo::exists(local)) { targetUrl = local; break; } } return targetUrl; } } diff --git a/plugins/clang/duchain/parsesession.cpp b/plugins/clang/duchain/parsesession.cpp index b75d47460c..bb7872012f 100644 --- a/plugins/clang/duchain/parsesession.cpp +++ b/plugins/clang/duchain/parsesession.cpp @@ -1,622 +1,622 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff Copyright 2013 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "parsesession.h" #include #include "clangdiagnosticevaluator.h" #include "todoextractor.h" #include "clanghelpers.h" #include "clangindex.h" #include "clangparsingenvironment.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "util/clangutils.h" #include "headerguardassistant.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { QVector extraArgs() { const auto extraArgsString = QString::fromLatin1(qgetenv("KDEV_CLANG_EXTRA_ARGUMENTS")); const auto extraArgs = KShell::splitArgs(extraArgsString); // transform to list of QByteArrays QVector result; result.reserve(extraArgs.size()); for (const QString& arg : extraArgs) { result << arg.toLatin1(); } clangDebug() << "Passing extra arguments to clang:" << result; return result; } void sanitizeArguments(QVector& arguments) { // We remove the -Werror flag, and replace -Werror=foo by -Wfoo. // Warning as error may cause problem to the clang parser. const auto asError = QByteArrayLiteral("-Werror="); const auto documentation = QByteArrayLiteral("-Wdocumentation"); for (auto& argument : arguments) { if (argument == "-Werror") { argument.clear(); } else if (argument.startsWith(asError)) { // replace -Werror=foo by -Wfoo argument.remove(2, asError.length() - 2); } #if CINDEX_VERSION_MINOR < 100 // FIXME https://bugs.llvm.org/show_bug.cgi?id=35333 if (argument == documentation) { argument.clear(); } #endif } } QVector argsForSession(const QString& path, ParseSessionData::Options options, const ParserSettings& parserSettings) { QMimeDatabase db; if (db.mimeTypeForFile(path).name() == QLatin1String("text/x-objcsrc")) { return {QByteArrayLiteral("-xobjective-c++")}; } // TODO: No proper mime type detection possible yet // cf. https://bugs.freedesktop.org/show_bug.cgi?id=26913 if (path.endsWith(QLatin1String(".cl"), Qt::CaseInsensitive)) { return {QByteArrayLiteral("-xcl")}; } // TODO: No proper mime type detection possible yet // cf. https://bugs.freedesktop.org/show_bug.cgi?id=23700 if (path.endsWith(QLatin1String(".cu"), Qt::CaseInsensitive) || path.endsWith(QLatin1String(".cuh"), Qt::CaseInsensitive)) { auto result = parserSettings.toClangAPI(); result.append(QByteArrayLiteral("-xcuda")); return result; } if (parserSettings.parserOptions.isEmpty()) { // The parserOptions can be empty for some unit tests that use ParseSession directly auto defaultArguments = ClangSettingsManager::self()->parserSettings(path).toClangAPI(); defaultArguments.append(QByteArrayLiteral("-nostdinc")); defaultArguments.append(QByteArrayLiteral("-nostdinc++")); defaultArguments.append(QByteArrayLiteral("-xc++")); sanitizeArguments(defaultArguments); return defaultArguments; } auto result = parserSettings.toClangAPI(); result.append(QByteArrayLiteral("-nostdinc")); if (parserSettings.isCpp()) { result.append(QByteArrayLiteral("-nostdinc++")); } if (options & ParseSessionData::PrecompiledHeader) { result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++-header") : QByteArrayLiteral("-xc-header")); sanitizeArguments(result); return result; } result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++") : QByteArrayLiteral("-xc")); sanitizeArguments(result); return result; } void addIncludes(QVector* args, QVector* otherArgs, const Path::List& includes, const char* cliSwitch) { for (const Path& url : includes) { if (url.isEmpty()) { continue; } QFileInfo info(url.toLocalFile()); QByteArray path = url.toLocalFile().toUtf8(); if (info.isFile()) { path.prepend("-include"); } else { path.prepend(cliSwitch); } otherArgs->append(path); args->append(path.constData()); } } void addFrameworkDirectories(QVector* args, QVector* otherArgs, const Path::List& frameworkDirectories, const char* cliSwitch) { for (const Path& url : frameworkDirectories) { if (url.isEmpty()) { continue; } QFileInfo info(url.toLocalFile()); if (!info.isDir()) { qCWarning(KDEV_CLANG) << "supposed framework directory is not a directory:" << url.pathOrUrl(); continue; } QByteArray path = url.toLocalFile().toUtf8(); otherArgs->append(cliSwitch); otherArgs->append(path); args->append(cliSwitch); args->append(path.constData()); } } QVector toClangApi(const QVector& unsavedFiles) { QVector unsaved; unsaved.reserve(unsavedFiles.size()); std::transform(unsavedFiles.begin(), unsavedFiles.end(), std::back_inserter(unsaved), [] (const UnsavedFile& file) { return file.toClangApi(); }); return unsaved; } bool hasQtIncludes(const Path::List& includePaths) { return std::find_if(includePaths.begin(), includePaths.end(), [] (const Path& path) { return path.lastPathSegment() == QLatin1String("QtCore"); }) != includePaths.end(); } } ParseSessionData::ParseSessionData(const QVector& unsavedFiles, ClangIndex* index, const ClangParsingEnvironment& environment, Options options) : m_file(nullptr) , m_unit(nullptr) { unsigned int flags = CXTranslationUnit_DetailedPreprocessingRecord #if CINDEX_VERSION_MINOR >= 34 | CXTranslationUnit_KeepGoing #endif ; if (options.testFlag(SkipFunctionBodies)) { flags |= CXTranslationUnit_SkipFunctionBodies; } if (options.testFlag(PrecompiledHeader)) { flags |= CXTranslationUnit_ForSerialization; } else { flags |= CXTranslationUnit_CacheCompletionResults #if CINDEX_VERSION_MINOR >= 32 | CXTranslationUnit_CreatePreambleOnFirstParse #endif | CXTranslationUnit_PrecompiledPreamble; if (environment.quality() == ClangParsingEnvironment::Unknown) { flags |= CXTranslationUnit_Incomplete; } } const auto tuUrl = environment.translationUnitUrl(); Q_ASSERT(!tuUrl.isEmpty()); const auto arguments = argsForSession(tuUrl.str(), options, environment.parserSettings()); QVector clangArguments; const auto& includes = environment.includes(); const auto& pchInclude = environment.pchInclude(); // uses QByteArray as smart-pointer for const char* ownership QVector smartArgs; smartArgs.reserve(includes.system.size() + includes.project.size() + pchInclude.isValid() + arguments.size() + 1); clangArguments.reserve(smartArgs.size()); std::transform(arguments.constBegin(), arguments.constEnd(), std::back_inserter(clangArguments), [] (const QByteArray &argument) { return argument.constData(); }); // NOTE: the PCH include must come before all other includes! if (pchInclude.isValid()) { clangArguments << "-include"; QByteArray pchFile = pchInclude.toLocalFile().toUtf8(); smartArgs << pchFile; clangArguments << pchFile.constData(); } if (hasQtIncludes(includes.system)) { const auto wrappedQtHeaders = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevclangsupport/wrappedQtHeaders"), QStandardPaths::LocateDirectory).toUtf8(); if (!wrappedQtHeaders.isEmpty()) { smartArgs << wrappedQtHeaders; clangArguments << "-isystem" << wrappedQtHeaders.constData(); const QByteArray qtCore = wrappedQtHeaders + "/QtCore"; smartArgs << qtCore; clangArguments << "-isystem" << qtCore.constData(); } } addIncludes(&clangArguments, &smartArgs, includes.system, "-isystem"); addIncludes(&clangArguments, &smartArgs, includes.project, "-I"); const auto& frameworkDirectories = environment.frameworkDirectories(); addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.system, "-iframework"); addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.project, "-F"); // libclang cannot find it's builtin dir automatically, we have to specify it manually smartArgs << ClangHelpers::clangBuiltinIncludePath().toUtf8(); clangArguments << "-isystem" << smartArgs.last().constData(); if (!environment.defines().isEmpty()) { smartArgs << writeDefinesFile(environment.defines()); clangArguments << "-imacros" << smartArgs.last().constData(); } if (!environment.workingDirectory().isEmpty()) { QByteArray workingDirectory = environment.workingDirectory().toLocalFile().toUtf8(); workingDirectory.prepend("-working-directory"); smartArgs << workingDirectory; clangArguments << workingDirectory.constData(); } // append extra args from environment variable static const auto extraArgs = ::extraArgs(); for (const QByteArray& arg : extraArgs) { clangArguments << arg.constData(); } QVector unsaved; //For PrecompiledHeader, we don't want unsaved contents (and contents.isEmpty()) if (!options.testFlag(PrecompiledHeader)) { unsaved = toClangApi(unsavedFiles); } // debugging: print hypothetical clang invocation including args (for easy c&p for local testing) if (qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_ARGS")) { QTextStream out(stdout); out << "Invocation: clang"; for (const auto& arg : qAsConst(clangArguments)) { out << " " << arg; } out << " " << tuUrl.byteArray().constData() << "\n"; } const CXErrorCode code = clang_parseTranslationUnit2( index->index(), tuUrl.byteArray().constData(), clangArguments.constData(), clangArguments.size(), unsaved.data(), unsaved.size(), flags, &m_unit ); if (code != CXError_Success) { qCWarning(KDEV_CLANG) << "clang_parseTranslationUnit2 return with error code" << code; if (!qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DIAGS")) { qCWarning(KDEV_CLANG) << " (start KDevelop with `KDEV_CLANG_DISPLAY_DIAGS=1 kdevelop` to see more diagnostics)"; } } if (m_unit) { setUnit(m_unit); m_environment = environment; if (options.testFlag(PrecompiledHeader)) { clang_saveTranslationUnit(m_unit, QByteArray(tuUrl.byteArray() + ".pch").constData(), CXSaveTranslationUnit_None); } } else { qCWarning(KDEV_CLANG) << "Failed to parse translation unit:" << tuUrl; } } ParseSessionData::~ParseSessionData() { clang_disposeTranslationUnit(m_unit); } QByteArray ParseSessionData::writeDefinesFile(const QMap& defines) { m_definesFile.open(); Q_ASSERT(m_definesFile.isWritable()); { QTextStream definesStream(&m_definesFile); // don't show warnings about redefined macros definesStream << "#pragma clang system_header\n"; for (auto it = defines.begin(); it != defines.end(); ++it) { if (it.key().startsWith(QLatin1String("__has_include(")) || it.key().startsWith(QLatin1String("__has_include_next("))) { continue; } definesStream << QLatin1String("#define ") << it.key() << ' ' << it.value() << '\n'; } } m_definesFile.close(); if (qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DEFINES")) { QFile f(m_definesFile.fileName()); f.open(QIODevice::ReadOnly); Q_ASSERT(f.isReadable()); QTextStream out(stdout); out << "Defines file: " << f.fileName() << "\n" << f.readAll() << f.size() << "\n VS defines:" << defines.size() << "\n"; } return m_definesFile.fileName().toUtf8(); } void ParseSessionData::setUnit(CXTranslationUnit unit) { m_unit = unit; m_diagnosticsCache.clear(); if (m_unit) { const ClangString unitFile(clang_getTranslationUnitSpelling(unit)); m_file = clang_getFile(m_unit, unitFile.c_str()); } else { m_file = nullptr; } } ClangParsingEnvironment ParseSessionData::environment() const { return m_environment; } ParseSession::ParseSession(const ParseSessionData::Ptr& data) : d(data) { if (d) { ENSURE_CHAIN_NOT_LOCKED d->m_mutex.lock(); } } ParseSession::~ParseSession() { if (d) { d->m_mutex.unlock(); } } void ParseSession::setData(const ParseSessionData::Ptr& data) { if (data == d) { return; } if (d) { d->m_mutex.unlock(); } d = data; if (d) { ENSURE_CHAIN_NOT_LOCKED d->m_mutex.lock(); } } ParseSessionData::Ptr ParseSession::data() const { return d; } IndexedString ParseSession::languageString() { static const IndexedString lang("Clang"); return lang; } ClangProblem::Ptr ParseSession::getOrCreateProblem(int indexInTU, CXDiagnostic diagnostic) const { auto& problem = d->m_diagnosticsCache[indexInTU]; if (!problem) { problem = ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit); } return problem; } ClangProblem::Ptr ParseSession::createExternalProblem(int indexInTU, CXDiagnostic diagnostic, const KLocalizedString& descriptionTemplate, int childProblemFinalLocationIndex) const { // Make a copy of the original (cached) problem since it is modified later auto problem = ClangProblem::Ptr(new ClangProblem(*getOrCreateProblem(indexInTU, diagnostic))); // Insert a copy of the parent problem (without child problems) as the first // child problem to preserve its location. auto* problemCopy = new ClangProblem(); problemCopy->setSource(problem->source()); problemCopy->setFinalLocation(problem->finalLocation()); problemCopy->setFinalLocationMode(problem->finalLocationMode()); problemCopy->setDescription(problem->description()); problemCopy->setExplanation(problem->explanation()); problemCopy->setSeverity(problem->severity()); auto childProblems = problem->diagnostics(); childProblems.prepend(IProblem::Ptr(problemCopy)); problem->setDiagnostics(childProblems); // Override the problem's finalLocation with that of the child problem in this document. // This is required to make the problem show up in the problem reporter for this // file, since it filters by finalLocation. It will also lead the user to the correct // location when clicking the problem and cause proper error highlighting. int index = (childProblemFinalLocationIndex >= 0) ? (1 + childProblemFinalLocationIndex) : (childProblems.size() - 1); problem->setFinalLocation(childProblems[index]->finalLocation()); problem->setDescription(descriptionTemplate.subs(problem->description()).toString()); return problem; } QList ParseSession::createRequestedHereProblems(int indexInTU, CXDiagnostic diagnostic, CXFile file) const { QList results; auto childDiagnostics = clang_getChildDiagnostics(diagnostic); auto numChildDiagnostics = clang_getNumDiagnosticsInSet(childDiagnostics); for (uint j = 0; j < numChildDiagnostics; ++j) { auto childDiagnostic = clang_getDiagnosticInSet(childDiagnostics, j); CXSourceLocation childLocation = clang_getDiagnosticLocation(childDiagnostic); CXFile childDiagnosticFile; clang_getFileLocation(childLocation, &childDiagnosticFile, nullptr, nullptr, nullptr); if (childDiagnosticFile == file) { QString description = ClangString(clang_getDiagnosticSpelling(childDiagnostic)).toString(); if (description.endsWith(QLatin1String("requested here"))) { // Note: Using the index j here assumes a 1:1 mapping from clang child diagnostics to KDevelop // problem diagnostics (i.e., child problems). If we wanted to avoid making this assumption, we'd have // to use ClangDiagnosticEvaluator::createProblem() first and then search within its // child problems to find the correct index. results << createExternalProblem(indexInTU, diagnostic, ki18n("Requested here: %1"), j); } } } return results; } QList ParseSession::problemsForFile(CXFile file) const { if (!d) { return {}; } QList problems; // extra clang diagnostics const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit); problems.reserve(numDiagnostics); d->m_diagnosticsCache.resize(numDiagnostics); for (uint i = 0; i < numDiagnostics; ++i) { auto diagnostic = clang_getDiagnostic(d->m_unit, i); CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); CXFile diagnosticFile; clang_getFileLocation(location, &diagnosticFile, nullptr, nullptr, nullptr); - auto requestedHereProblems = createRequestedHereProblems(i, diagnostic, file); + const auto requestedHereProblems = createRequestedHereProblems(i, diagnostic, file); for (const auto& ptr : requestedHereProblems) { problems.append(static_cast(ptr)); } // missing-include problems are so severe in clang that we always propagate // them to this document, to ensure that the user will see the error. if (diagnosticFile != file && ClangDiagnosticEvaluator::diagnosticType(diagnostic) != ClangDiagnosticEvaluator::IncludeFileNotFoundProblem) { continue; } problems << ((diagnosticFile == file) ? getOrCreateProblem(i, diagnostic) : createExternalProblem(i, diagnostic, ki18n("In included file: %1"))); clang_disposeDiagnostic(diagnostic); } // other problem sources TodoExtractor extractor(unit(), file); problems << extractor.problems(); #if CINDEX_VERSION_MINOR > 30 // note that the below warning is triggered on every reparse when there is a precompiled preamble // see also TestDUChain::testReparseIncludeGuard const QString path = QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath(); const IndexedString indexedPath(path); if (ClangHelpers::isHeader(path) && !clang_isFileMultipleIncludeGuarded(unit(), file) && !clang_Location_isInSystemHeader(clang_getLocationForOffset(d->m_unit, file, 0))) { QExplicitlySharedDataPointer problem(new StaticAssistantProblem); problem->setSeverity(IProblem::Warning); problem->setDescription(i18n("Header is not guarded against multiple inclusions")); problem->setExplanation(i18n("The given header is not guarded against multiple inclusions, " "either with the conventional #ifndef/#define/#endif macro guards or with #pragma once.")); const KTextEditor::Range problemRange(0, 0, KDevelop::createCodeRepresentation(indexedPath)->lines(), 0); problem->setFinalLocation(DocumentRange{indexedPath, problemRange}); problem->setSource(IProblem::Preprocessor); problem->setSolutionAssistant(KDevelop::IAssistant::Ptr(new HeaderGuardAssistant(d->m_unit, file))); problems << problem; } #endif return problems; } CXTranslationUnit ParseSession::unit() const { return d ? d->m_unit : nullptr; } CXFile ParseSession::file(const QByteArray& path) const { return clang_getFile(unit(), path.constData()); } CXFile ParseSession::mainFile() const { return d ? d->m_file : nullptr; } bool ParseSession::reparse(const QVector& unsavedFiles, const ClangParsingEnvironment& environment) { if (!d || environment != d->m_environment) { return false; } auto unsaved = toClangApi(unsavedFiles); const auto code = clang_reparseTranslationUnit(d->m_unit, unsaved.size(), unsaved.data(), clang_defaultReparseOptions(d->m_unit)); if (code != CXError_Success) { qCWarning(KDEV_CLANG) << "clang_reparseTranslationUnit return with error code" << code; // if error code != 0 => clang_reparseTranslationUnit invalidates the old translation unit => clean up clang_disposeTranslationUnit(d->m_unit); d->setUnit(nullptr); return false; } // update state d->setUnit(d->m_unit); return true; } ClangParsingEnvironment ParseSession::environment() const { return d->m_environment; } diff --git a/plugins/clang/kdevclang.xml b/plugins/clang/kdevclang.xml index e0880469a2..68ae02a7ea 100644 --- a/plugins/clang/kdevclang.xml +++ b/plugins/clang/kdevclang.xml @@ -1,98 +1,92 @@ OpenCL C source code Codi font en C per a OpenCL Codi font en C per a OpenCL Zdrojový kód OpenCL C OpenCL C Quelltext - OpenCL C πηγαίος κώδικας OpenCL C source code Código fuente de OpenCL C - OpenCL C lähtekood Code source C OpenCL Código fonte en C de OpenCL Codice sorgente OpenCL C OpenCL C 원본 코드 OpenCL C broncode Kod źródłowy OpenCL C Código-fonte em C do OpenCL - Código-fonte em C do OpenCL + Código fonte C do OpenCL Исходный код OpenCL C Zdrojový kód OpenCL C OpenCL C-källkod OpenCL C kaynak kodu початковий код C OpenCL OpenCL C 源代码 OpenCL C 來源程式碼 OpenCL Open Computing Language NVIDIA CUDA C source code Codi font en C per a CUDA de NVIDIA Codi font en C per a CUDA de NVIDIA Zdrojový kód NVIDIA CUDA C NVIDIA CUDA C Quelltext - NVIDIA CUDA C πηγαίος κώδικας NVIDIA CUDA C source code Código fuente de NVIDIA CUDA C - NVIDIA CUDA C lähtekood Code source C CUDA NVIDIA Código fonte en C de NVIDIA CUDA Codice sorgente CUDA C di NVIDIA NVIDIA CUDA C 원본 코드 NVIDIA CUDA C broncode Kod źródłowy NVIDIA CUDA C Código-fonte em CUDA C da NVIDIA - Código-fonte em CUDA C da NVIDIA + Código fonte C da NVIDIA CUDA Исходный код NVIDIA CUDA C Zdrojový kód NVIDIA CUDA C NVIDIA CUDA C-källkod NVIDIA CUDA C kaynak kodu початковий код C NVIDIA CUDA NVIDIA CUDA C 源代码 NVIDIA CUDA C 來源程式碼 NVIDIA CUDA C header Capçalera en C per a CUDA de NVIDIA Capçalera en C per a CUDA de NVIDIA Hlavičkový soubor NVIDIA CUDA C NVIDIA CUDA C Header - NVIDIA CUDA C κεφαλίδα NVIDIA CUDA C header Cabecera de NVIDIA CUDA C - NVIDIA CUDA C päis En-tête C CUDA NVIDIA Cabeceira en C de NVIDIA CUDA Intestazione CUDA C di NVIDIA NVIDIA CUDA C 헤더 NVIDIA CUDA C header Nagłówek NVIDIA CUDA C Ficheiro de inclusão em CUDA C da NVIDIA Cabeçalho C da NVIDIA CUDA Заголовочный файл NVIDIA CUDA C Hlavička NVIDIA CUDA C NVIDIA CUDA C-deklarationsfil файл заголовків C NVIDIA CUDA NVIDIA CUDA C 头文件 NVIDIA CUDA C 標頭檔 diff --git a/plugins/clang/kdevclangsupport.json b/plugins/clang/kdevclangsupport.json index a3495cf3c5..dbe13c67b0 100644 --- a/plugins/clang/kdevclangsupport.json +++ b/plugins/clang/kdevclangsupport.json @@ -1,82 +1,78 @@ { "KPlugin": { "Description": "C/C++ Language Support (Clang-based)", "Description[ca@valencia]": "Implementació del llenguatge C/C++ (basat en Clang)", "Description[ca]": "Implementació del llenguatge C/C++ (basat en Clang)", "Description[cs]": "Podpora jazyka C/C++ (založeno na Clang)", "Description[de]": "Sprachunterstützung für C/C++ (Clang-basiert)", - "Description[el]": "Υποστήριξη γλώσσας C/C++ (με βάση τη Clang)", "Description[en_GB]": "C/C++ Language Support (Clang-based)", "Description[es]": "Implementación del lenguaje C/C++ (basado en Clang)", - "Description[et]": "C/C++ keele toetus (põhineb Clangil)", + "Description[et]": "C/C++ keele toetus (Clangi baasil)", "Description[fi]": "C/C++-kielituki (Clang-pohjainen)", "Description[fr]": "Prise en charge du langage C/C++ (utilisant Clang)", "Description[gl]": "Compatibilidade con C e C++ (baseada en Clang)", "Description[it]": "Supporto al linguaggio C/C++ (basato su Clang)", "Description[nl]": "Ondersteuning voor de talen C/C++ (Clang-based)", "Description[pl]": "Obsługa języka C/C++ (oparta na Clang)", "Description[pt]": "Suporte para a Linguagem C/C++ (baseado no Clang)", "Description[pt_BR]": "Suporte à linguagem C/C++ (baseado no Clang)", "Description[sk]": "Podpora jazyka C/C++ (založená na triedach)", "Description[sl]": "Podpora jeziku C/C++ (temelječa na Clang)", "Description[sv]": "C/C++ språkstöd (Clang-baserat)", "Description[tr]": "C/C++ Dil Desteği (Clang-tabanlı)", "Description[uk]": "Підтримка мови C/C++ на основі Clang", "Description[x-test]": "xxC/C++ Language Support (Clang-based)xx", "Description[zh_CN]": "C/C++ 语言支持 (基于 Clang)", "Icon": "text-x-c++src", "Id": "kdevclangsupport", "Name": "C++ Support", - "Name[bs]": "C++ Podrška", "Name[ca@valencia]": "Implementació del C++", "Name[ca]": "Implementació del C++", "Name[cs]": "Podpora C++", "Name[de]": "Unterstützung für C++", - "Name[el]": "Υποστήριξη C++", "Name[en_GB]": "C++ Support", "Name[es]": "Implementación de C++", "Name[et]": "C++ toetus", "Name[fi]": "C++-tuki", "Name[fr]": "Prise en charge du C++", "Name[gl]": "Compatibilidade con C++", - "Name[hu]": "C++ támogatás", "Name[it]": "Supporto per C++", "Name[nl]": "Ondersteuning voor C++", "Name[pl]": "Obsługa C++", "Name[pt]": "Suporte para o C++", "Name[pt_BR]": "Suporte à C++", "Name[ru]": "Поддержка C++", "Name[sk]": "Podpora C++", "Name[sl]": "Podpora za C++", "Name[sv]": "C++ stöd", "Name[tr]": "C++ Desteği", "Name[uk]": "Підтримка C++", "Name[x-test]": "xxC++ Supportxx", "Name[zh_CN]": "C++ 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "ILanguageSupport" ], "X-KDevelop-Languages": [ "C", "C++", "OpenCL C", "CUDA C", "Objective-C" ], "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-chdr", "text/x-c++hdr", "text/x-csrc", "text/x-c++src", "text/x-opencl-src", "text/vnd.nvidia.cuda.csrc", "text/vnd.nvidia.cuda.chdr", "text/x-objcsrc" ] } diff --git a/plugins/clang/tests/test_duchain.cpp b/plugins/clang/tests/test_duchain.cpp index 57866bff69..1614ec753f 100644 --- a/plugins/clang/tests/test_duchain.cpp +++ b/plugins/clang/tests/test_duchain.cpp @@ -1,2228 +1,2228 @@ /* * Copyright 2014 Milian Wolff * Copyright 2014 Kevin Funk * Copyright 2015 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_duchain.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "duchain/clangparsingenvironmentfile.h" #include "duchain/clangparsingenvironment.h" #include "duchain/parsesession.h" #include "duchain/clanghelpers.h" #include "testprovider.h" #include #include #include #include #include #include QTEST_MAIN(TestDUChain) using namespace KDevelop; TestDUChain::~TestDUChain() = default; void TestDUChain::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.plugins.clang.debug=true\n")); QVERIFY(qputenv("KDEV_CLANG_DISPLAY_DIAGS", "1")); AutoTestShell::init({QStringLiteral("kdevclangsupport")}); auto core = TestCore::initialize(); delete core->projectController(); m_projectController = new TestProjectController(core); core->setProjectController(m_projectController); } void TestDUChain::cleanupTestCase() { TestCore::shutdown(); } void TestDUChain::cleanup() { if (m_provider) { IDefinesAndIncludesManager::manager()->unregisterBackgroundProvider(m_provider.data()); } } void TestDUChain::init() { m_provider.reset(new TestEnvironmentProvider); IDefinesAndIncludesManager::manager()->registerBackgroundProvider(m_provider.data()); } struct ExpectedComment { QString identifier; QString comment; }; Q_DECLARE_METATYPE(ExpectedComment) Q_DECLARE_METATYPE(AbstractType::WhichType) void TestDUChain::testComments() { QFETCH(QString, code); QFETCH(ExpectedComment, expectedComment); TestFile file(code, QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); auto candidates = top->findDeclarations(QualifiedIdentifier(expectedComment.identifier)); QVERIFY(!candidates.isEmpty()); auto decl = candidates.first(); QString comment = QString::fromLocal8Bit(decl->comment()); const auto plainText = KDevelop::htmlToPlainText(comment, KDevelop::CompleteMode); // if comment is e.g. "("code"); QTest::addColumn("expectedComment"); // note: Clang only retrieves the comments when in doxygen-style format (i.e. '///', '/**', '///<') QTest::newRow("invalid1") << "//this is foo\nint foo;" << ExpectedComment{"foo", QString()}; QTest::newRow("invalid2") << "/*this is foo*/\nint foo;" << ExpectedComment{"foo", QString()}; QTest::newRow("basic1") << "///this is foo\nint foo;" << ExpectedComment{"foo", "this is foo"}; QTest::newRow("basic2") << "/**this is foo*/\nint foo;" << ExpectedComment{"foo", "this is foo"}; // as long as https://bugs.llvm.org/show_bug.cgi?id=35333 is not fixed, we don't fully parse and render // doxygen-style comments properly (cf. `makeComment` in builder.cpp) #define PARSE_COMMENTS 0 QTest::newRow("enumerator") << "enum Foo { bar1, ///localDeclarations().size(), 2); auto decl = file.topContext()->localDeclarations()[1]; QVERIFY(decl); auto function = dynamic_cast(decl); QVERIFY(function); auto functionType = function->type(); QVERIFY(functionType); #if CINDEX_VERSION_MINOR < 34 QEXPECT_FAIL("namespace", "The ElaboratedType is not exposed through the libclang interface, not much we can do here", Abort); #endif QVERIFY(functionType->returnType()->whichType() != AbstractType::TypeDelayed); #if CINDEX_VERSION_MINOR < 34 QEXPECT_FAIL("typedef", "After using clang_getCanonicalType on ElaboratedType all typedef information get's stripped away", Continue); #endif QCOMPARE(functionType->returnType()->whichType(), type); } void TestDUChain::testElaboratedType_data() { QTest::addColumn("code"); QTest::addColumn("type"); QTest::newRow("namespace") << "namespace NS{struct Type{};} struct NS::Type foo();" << AbstractType::TypeStructure; QTest::newRow("enum") << "enum Enum{}; enum Enum foo();" << AbstractType::TypeEnumeration; QTest::newRow("typedef") << "namespace NS{typedef int type;} NS::type foo();" << AbstractType::TypeAlias; } void TestDUChain::testInclude() { TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h")); // NOTE: header is _not_ explicitly being parsed, instead the impl job does that TestFile impl("#include \"" + header.url().str() + "\"\n" "int main() { return foo(); }", QStringLiteral("cpp"), &header); impl.parse(TopDUContext::AllDeclarationsContextsAndUses); auto implCtx = impl.topContext(); QVERIFY(implCtx); DUChainReadLocker lock; QCOMPARE(implCtx->localDeclarations().size(), 1); auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); QVERIFY(!headerCtx->parsingEnvironmentFile()->needsUpdate()); QCOMPARE(headerCtx->localDeclarations().size(), 1); QVERIFY(implCtx->imports(headerCtx, CursorInRevision(0, 10))); Declaration* foo = headerCtx->localDeclarations().first(); QCOMPARE(foo->uses().size(), 1); QCOMPARE(foo->uses().begin().key(), impl.url()); QCOMPARE(foo->uses().begin()->size(), 1); QCOMPARE(foo->uses().begin()->first(), RangeInRevision(1, 20, 1, 23)); } void TestDUChain::testMissingInclude() { auto code = R"( #pragma once #include "missing1.h" template class A { T a; }; #include "missing2.h" class B : public A { }; )"; TestFile header(code, QStringLiteral("h")); TestFile impl("#include \"" + header.url().str() + "\"\n", QStringLiteral("cpp"), &header); QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses)); DUChainReadLocker lock; auto top = impl.topContext(); QVERIFY(top); QCOMPARE(top->importedParentContexts().count(), 1); TopDUContext* headerCtx = dynamic_cast(top->importedParentContexts().first().context(top)); QVERIFY(headerCtx); QCOMPARE(headerCtx->url(), header.url()); #if CINDEX_VERSION_MINOR < 34 QEXPECT_FAIL("", "Second missing header isn't reported", Continue); #endif QCOMPARE(headerCtx->problems().count(), 2); QCOMPARE(headerCtx->localDeclarations().count(), 2); auto a = dynamic_cast(headerCtx->localDeclarations().first()); QVERIFY(a); auto b = dynamic_cast(headerCtx->localDeclarations().last()); QVERIFY(b); // NOTE: This fails and needs fixing. If the include of "missing2.h" // above is commented out, then it doesn't fail. Maybe // clang stops processing when it encounters the second missing // header, or similar. // XFAIL this check until https://bugs.llvm.org/show_bug.cgi?id=38155 is fixed if (QVersionNumber::fromString(ClangHelpers::clangVersion()) < QVersionNumber(9, 0, 0)) QEXPECT_FAIL("", "Base class isn't assigned correctly", Continue); QCOMPARE(b->baseClassesSize(), 1u); #if CINDEX_VERSION_MINOR < 34 // at least the one problem we have should have been propagated QCOMPARE(top->problems().count(), 1); #else // two errors: // /tmp/testfile_f32415.h:3:10: error: 'missing1.h' file not found // /tmp/testfile_f32415.h:11:10: error: 'missing2.h' file not found QCOMPARE(top->problems().count(), 2); #endif } QByteArray createCode(const QByteArray& prefix, const int functions) { QByteArray code; code += "#ifndef " + prefix + "_H\n"; code += "#define " + prefix + "_H\n"; for (int i = 0; i < functions; ++i) { code += "void myFunc_" + prefix + "(int arg1, char arg2, const char* arg3);\n"; } code += "#endif\n"; return code; } void TestDUChain::testIncludeLocking() { TestFile header1(createCode("Header1", 1000), QStringLiteral("h")); TestFile header2(createCode("Header2", 1000), QStringLiteral("h")); TestFile header3(createCode("Header3", 1000), QStringLiteral("h")); ICore::self()->languageController()->backgroundParser()->setThreadCount(3); TestFile impl1("#include \"" + header1.url().str() + "\"\n" "#include \"" + header2.url().str() + "\"\n" "#include \"" + header3.url().str() + "\"\n" "int main() { return 0; }", QStringLiteral("cpp")); TestFile impl2("#include \"" + header2.url().str() + "\"\n" "#include \"" + header1.url().str() + "\"\n" "#include \"" + header3.url().str() + "\"\n" "int main() { return 0; }", QStringLiteral("cpp")); TestFile impl3("#include \"" + header3.url().str() + "\"\n" "#include \"" + header1.url().str() + "\"\n" "#include \"" + header2.url().str() + "\"\n" "int main() { return 0; }", QStringLiteral("cpp")); impl1.parse(TopDUContext::AllDeclarationsContextsAndUses); impl2.parse(TopDUContext::AllDeclarationsContextsAndUses); impl3.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(impl1.waitForParsed(5000)); QVERIFY(impl2.waitForParsed(5000)); QVERIFY(impl3.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(DUChain::self()->chainForDocument(header1.url())); QVERIFY(DUChain::self()->chainForDocument(header2.url())); QVERIFY(DUChain::self()->chainForDocument(header3.url())); } void TestDUChain::testReparse() { TestFile file(QStringLiteral("int main() { int i = 42; return i; }"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); DeclarationPointer mainDecl; DeclarationPointer iDecl; for (int i = 0; i < 3; ++i) { QVERIFY(file.waitForParsed(500)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 1); QCOMPARE(file.topContext()->localDeclarations().size(), 1); DUContext *exprContext = file.topContext()->childContexts().first()->childContexts().first(); QCOMPARE(exprContext->localDeclarations().size(), 1); if (i) { QVERIFY(mainDecl); QCOMPARE(mainDecl.data(), file.topContext()->localDeclarations().first()); QVERIFY(iDecl); QCOMPARE(iDecl.data(), exprContext->localDeclarations().first()); } mainDecl = file.topContext()->localDeclarations().first(); iDecl = exprContext->localDeclarations().first(); QVERIFY(mainDecl->uses().isEmpty()); QCOMPARE(iDecl->uses().size(), 1); QCOMPARE(iDecl->uses().begin()->size(), 1); if (i == 1) { file.setFileContents(QStringLiteral("int main()\n{\nfloat i = 13; return i - 5;\n}\n")); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testReparseError() { TestFile file(QStringLiteral("int i = 1 / 0;\n"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); for (int i = 0; i < 2; ++i) { QVERIFY(file.waitForParsed(500)); DUChainReadLocker lock; QVERIFY(file.topContext()); if (!i) { QCOMPARE(file.topContext()->problems().size(), 1); file.setFileContents(QStringLiteral("int i = 0;\n")); } else { QCOMPARE(file.topContext()->problems().size(), 0); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testTemplate() { TestFile file("template struct foo { T bar; };\n" "int main() { foo myFoo; return myFoo.bar; }\n", QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto fooDecl = file.topContext()->localDeclarations().first(); QVERIFY(fooDecl->internalContext()); QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 2); QCOMPARE(file.topContext()->findDeclarations(QualifiedIdentifier("foo< T >")).size(), 1); QCOMPARE(file.topContext()->findDeclarations(QualifiedIdentifier("foo< T >::bar")).size(), 1); auto mainCtx = file.topContext()->localDeclarations().last()->internalContext()->childContexts().first(); QVERIFY(mainCtx); auto myFoo = mainCtx->localDeclarations().first(); QVERIFY(myFoo); QCOMPARE(myFoo->abstractType()->toString().remove(' '), QStringLiteral("foo")); } void TestDUChain::testNamespace() { TestFile file("namespace foo { struct bar { int baz; }; }\n" "int main() { foo::bar myBar; }\n", QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto fooDecl = file.topContext()->localDeclarations().first(); QVERIFY(fooDecl->internalContext()); QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 1); DUContext* top = file.topContext().data(); DUContext* mainCtx = file.topContext()->childContexts().last(); auto foo = top->localDeclarations().first(); QCOMPARE(foo->qualifiedIdentifier().toString(), QString("foo")); DUContext* fooCtx = file.topContext()->childContexts().first(); QCOMPARE(fooCtx->localScopeIdentifier().toString(), QString("foo")); QCOMPARE(fooCtx->scopeIdentifier(true).toString(), QString("foo")); QCOMPARE(fooCtx->localDeclarations().size(), 1); auto bar = fooCtx->localDeclarations().first(); QCOMPARE(bar->qualifiedIdentifier().toString(), QString("foo::bar")); QCOMPARE(fooCtx->childContexts().size(), 1); DUContext* barCtx = fooCtx->childContexts().first(); QCOMPARE(barCtx->localScopeIdentifier().toString(), QString("bar")); QCOMPARE(barCtx->scopeIdentifier(true).toString(), QString("foo::bar")); QCOMPARE(barCtx->localDeclarations().size(), 1); auto baz = barCtx->localDeclarations().first(); QCOMPARE(baz->qualifiedIdentifier().toString(), QString("foo::bar::baz")); for (auto ctx : {top, mainCtx}) { QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo")).size(), 1); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo::bar")).size(), 1); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo::bar::baz")).size(), 1); } } void TestDUChain::testAutoTypeDeduction() { TestFile file(QStringLiteral(R"( const volatile auto foo = 5; template struct myTemplate {}; myTemplate& > templRefParam; auto autoTemplRefParam = templRefParam; )"), QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* ctx = file.topContext().data(); QVERIFY(ctx); QCOMPARE(ctx->localDeclarations().size(), 4); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo")).size(), 1); Declaration* decl = ctx->findDeclarations(QualifiedIdentifier(QStringLiteral("foo")))[0]; QCOMPARE(decl->identifier(), Identifier("foo")); #if CINDEX_VERSION_MINOR < 31 QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue); #endif QVERIFY(decl->type()); #if CINDEX_VERSION_MINOR < 31 QCOMPARE(decl->toString(), QStringLiteral("const volatile auto foo")); #else QCOMPARE(decl->toString(), QStringLiteral("const volatile int foo")); #endif decl = ctx->findDeclarations(QualifiedIdentifier(QStringLiteral("autoTemplRefParam")))[0]; QVERIFY(decl); QVERIFY(decl->abstractType()); #if CINDEX_VERSION_MINOR < 31 QEXPECT_FAIL("", "Auto type is not exposed via LibClang", Continue); #endif QCOMPARE(decl->abstractType()->toString(), QStringLiteral("myTemplate< myTemplate< int >& >")); } void TestDUChain::testTypeDeductionInTemplateInstantiation() { // see: http://clang-developers.42468.n3.nabble.com/RFC-missing-libclang-query-functions-features-td2504253.html TestFile file(QStringLiteral("template struct foo { T member; } foo f; auto i = f.member;"), QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* ctx = file.topContext().data(); QVERIFY(ctx); QCOMPARE(ctx->localDeclarations().size(), 3); Declaration* decl = nullptr; // check 'foo' declaration decl = ctx->localDeclarations()[0]; QVERIFY(decl); QCOMPARE(decl->identifier(), Identifier("foo< T >")); // check type of 'member' inside declaration-scope QCOMPARE(ctx->childContexts().size(), 1); DUContext* fooCtx = ctx->childContexts().first(); QVERIFY(fooCtx); // Should there really be two declarations? QCOMPARE(fooCtx->localDeclarations().size(), 2); decl = fooCtx->localDeclarations()[1]; QCOMPARE(decl->identifier(), Identifier("member")); // check type of 'member' in definition of 'f' decl = ctx->localDeclarations()[1]; QCOMPARE(decl->identifier(), Identifier("f")); decl = ctx->localDeclarations()[2]; QCOMPARE(decl->identifier(), Identifier("i")); #if CINDEX_VERSION_MINOR < 31 QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue); #endif QVERIFY(decl->type()); } void TestDUChain::testVirtualMemberFunction() { //Forward-declarations with "struct" or "class" are considered equal, so make sure the override is detected correctly. TestFile file(QStringLiteral("struct S {}; struct A { virtual S* ret(); }; struct B : public A { virtual S* ret(); };"), QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* top = file.topContext().data(); QVERIFY(top); QCOMPARE(top->childContexts().count(), 3); QCOMPARE(top->localDeclarations().count(), 3); QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1); Declaration* decl = top->childContexts()[2]->localDeclarations()[0]; QCOMPARE(decl->identifier(), Identifier("ret")); QVERIFY(DUChainUtils::overridden(decl)); } void TestDUChain::testBaseClasses() { TestFile file(QStringLiteral("class Base {}; class Inherited : public Base {};"), QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* top = file.topContext().data(); QVERIFY(top); QCOMPARE(top->localDeclarations().count(), 2); Declaration* baseDecl = top->localDeclarations().first(); QCOMPARE(baseDecl->identifier(), Identifier("Base")); ClassDeclaration* inheritedDecl = dynamic_cast(top->localDeclarations()[1]); QCOMPARE(inheritedDecl->identifier(), Identifier("Inherited")); QVERIFY(inheritedDecl); QCOMPARE(inheritedDecl->baseClassesSize(), 1u); QCOMPARE(baseDecl->uses().count(), 1); QCOMPARE(baseDecl->uses().first().count(), 1); QCOMPARE(baseDecl->uses().first().first(), RangeInRevision(0, 40, 0, 44)); } void TestDUChain::testReparseBaseClasses() { TestFile file(QStringLiteral("struct a{}; struct b : a {};\n"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); for (int i = 0; i < 2; ++i) { qDebug() << "run: " << i; QVERIFY(file.waitForParsed(500)); DUChainWriteLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->childContexts().first()->importers().size(), 1); QCOMPARE(file.topContext()->childContexts().last()->importedParentContexts().size(), 1); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto aDecl = dynamic_cast(file.topContext()->localDeclarations().first()); QVERIFY(aDecl); QCOMPARE(aDecl->baseClassesSize(), 0u); auto bDecl = dynamic_cast(file.topContext()->localDeclarations().last()); QVERIFY(bDecl); QCOMPARE(bDecl->baseClassesSize(), 1u); int distance = 0; QVERIFY(bDecl->isPublicBaseClass(aDecl, file.topContext(), &distance)); QCOMPARE(distance, 1); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testReparseBaseClassesTemplates() { TestFile file(QStringLiteral("template struct a{}; struct b : a {};\n"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); for (int i = 0; i < 2; ++i) { qDebug() << "run: " << i; QVERIFY(file.waitForParsed(500)); DUChainWriteLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->childContexts().first()->importers().size(), 1); QCOMPARE(file.topContext()->childContexts().last()->importedParentContexts().size(), 1); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto aDecl = dynamic_cast(file.topContext()->localDeclarations().first()); QVERIFY(aDecl); QCOMPARE(aDecl->baseClassesSize(), 0u); auto bDecl = dynamic_cast(file.topContext()->localDeclarations().last()); QVERIFY(bDecl); QCOMPARE(bDecl->baseClassesSize(), 1u); int distance = 0; QVERIFY(bDecl->isPublicBaseClass(aDecl, file.topContext(), &distance)); QCOMPARE(distance, 1); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testGetInheriters_data() { QTest::addColumn("code"); QTest::newRow("inline") << "struct Base { struct Inner {}; }; struct Inherited : Base, Base::Inner {};"; QTest::newRow("outline") << "struct Base { struct Inner; }; struct Base::Inner {}; struct Inherited : Base, Base::Inner {};"; } void TestDUChain::testGetInheriters() { QFETCH(QString, code); TestFile file(code, QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); QVERIFY(top->problems().isEmpty()); QCOMPARE(top->localDeclarations().count(), 2); Declaration* baseDecl = top->localDeclarations().first(); QCOMPARE(baseDecl->identifier(), Identifier("Base")); DUContext* baseCtx = baseDecl->internalContext(); QVERIFY(baseCtx); QCOMPARE(baseCtx->localDeclarations().count(), 1); Declaration* innerDecl = baseCtx->localDeclarations().first(); QCOMPARE(innerDecl->identifier(), Identifier("Inner")); if (auto forward = dynamic_cast(innerDecl)) { innerDecl = forward->resolve(top); } QVERIFY(dynamic_cast(innerDecl)); Declaration* inheritedDecl = top->localDeclarations().last(); QVERIFY(inheritedDecl); QCOMPARE(inheritedDecl->identifier(), Identifier("Inherited")); uint maxAllowedSteps = uint(-1); auto baseInheriters = DUChainUtils::inheriters(baseDecl, maxAllowedSteps); QCOMPARE(baseInheriters, QList() << inheritedDecl); maxAllowedSteps = uint(-1); auto innerInheriters = DUChainUtils::inheriters(innerDecl, maxAllowedSteps); QCOMPARE(innerInheriters, QList() << inheritedDecl); maxAllowedSteps = uint(-1); auto inheritedInheriters = DUChainUtils::inheriters(inheritedDecl, maxAllowedSteps); QCOMPARE(inheritedInheriters.count(), 0); } void TestDUChain::testGlobalFunctionDeclaration() { TestFile file(QStringLiteral("void foo(int arg1, char arg2);\n"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); file.waitForParsed(); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 1); QCOMPARE(file.topContext()->childContexts().size(), 1); QVERIFY(!file.topContext()->childContexts().first()->inSymbolTable()); } void TestDUChain::testFunctionDefinitionVsDeclaration() { TestFile file(QStringLiteral("void func(); void func() {}\n"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto funcDecl = file.topContext()->localDeclarations()[0]; QVERIFY(!funcDecl->isDefinition()); QVERIFY(!dynamic_cast(funcDecl)); auto funcDef = file.topContext()->localDeclarations()[1]; QVERIFY(dynamic_cast(funcDef)); QVERIFY(funcDef->isDefinition()); } void TestDUChain::testEnsureNoDoubleVisit() { // On some language construct, we may up visiting the same cursor multiple times // Example: "struct SomeStruct {} s;" // decl: "SomeStruct SomeStruct " of kind StructDecl (2) in main.cpp@[(1,1),(1,17)] // decl: "struct SomeStruct s " of kind VarDecl (9) in main.cpp@[(1,1),(1,19)] // decl: "SomeStruct SomeStruct " of kind StructDecl (2) in main.cpp@[(1,1),(1,17)] // // => We end up visiting the StructDecl twice (or more) // That's because we use clang_visitChildren not just on the translation unit cursor. // Apparently just "recursing" vs. "visiting children explicitly" // results in a different AST traversal TestFile file(QStringLiteral("struct SomeStruct {} s;\n"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); // there should only be one declaration for "SomeStruct" auto candidates = top->findDeclarations(QualifiedIdentifier(QStringLiteral("SomeStruct"))); QCOMPARE(candidates.size(), 1); } void TestDUChain::testParsingEnvironment() { const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses; IndexedTopDUContext indexed; ClangParsingEnvironment lastEnv; { TestFile file(QStringLiteral("int main() {}\n"), QStringLiteral("cpp")); auto astFeatures = static_cast(features | TopDUContext::AST); file.parse(astFeatures); file.setKeepDUChainData(true); QVERIFY(file.waitForParsed()); DUChainWriteLocker lock; auto top = file.topContext(); QVERIFY(top); auto sessionData = ParseSessionData::Ptr(dynamic_cast(top->ast().data())); lock.unlock(); ParseSession session(sessionData); lock.lock(); QVERIFY(session.data()); QVERIFY(top); auto envFile = QExplicitlySharedDataPointer( dynamic_cast(file.topContext()->parsingEnvironmentFile().data())); QCOMPARE(envFile->features(), astFeatures); QVERIFY(envFile->featuresSatisfied(astFeatures)); QCOMPARE(envFile->environmentQuality(), ClangParsingEnvironment::Source); // if no environment is given, no update should be triggered QVERIFY(!envFile->needsUpdate()); // same env should also not trigger a reparse ClangParsingEnvironment env = session.environment(); QCOMPARE(env.quality(), ClangParsingEnvironment::Source); QVERIFY(!envFile->needsUpdate(&env)); // but changing the environment should trigger an update env.addIncludes(Path::List() << Path(QStringLiteral("/foo/bar/baz"))); QVERIFY(envFile->needsUpdate(&env)); envFile->setEnvironment(env); QVERIFY(!envFile->needsUpdate(&env)); // setting the environment quality higher should require an update env.setQuality(ClangParsingEnvironment::BuildSystem); QVERIFY(envFile->needsUpdate(&env)); envFile->setEnvironment(env); QVERIFY(!envFile->needsUpdate(&env)); // changing defines requires an update env.addDefines(QHash{ { "foo", "bar" } }); QVERIFY(envFile->needsUpdate(&env)); // but only when changing the defines for the envFile's TU const auto barTU = IndexedString("bar.cpp"); const auto oldTU = env.translationUnitUrl(); env.setTranslationUnitUrl(barTU); QCOMPARE(env.translationUnitUrl(), barTU); QVERIFY(!envFile->needsUpdate(&env)); env.setTranslationUnitUrl(oldTU); QVERIFY(envFile->needsUpdate(&env)); // update it again envFile->setEnvironment(env); QVERIFY(!envFile->needsUpdate(&env)); lastEnv = env; // now compare against a lower quality environment // in such a case, we do not want to trigger an update env.setQuality(ClangParsingEnvironment::Unknown); env.setTranslationUnitUrl(barTU); QVERIFY(!envFile->needsUpdate(&env)); // even when the environment changes env.addIncludes(Path::List() << Path(QStringLiteral("/lalalala"))); QVERIFY(!envFile->needsUpdate(&env)); indexed = top->indexed(); } DUChain::self()->storeToDisk(); { DUChainWriteLocker lock; QVERIFY(!DUChain::self()->isInMemory(indexed.index())); QVERIFY(indexed.data()); QVERIFY(DUChain::self()->environmentFileForDocument(indexed)); auto envFile = QExplicitlySharedDataPointer( dynamic_cast(DUChain::self()->environmentFileForDocument(indexed).data())); QVERIFY(envFile); QCOMPARE(envFile->features(), features); QVERIFY(envFile->featuresSatisfied(features)); QVERIFY(!envFile->needsUpdate(&lastEnv)); DUChain::self()->removeDocumentChain(indexed.data()); } } void TestDUChain::testActiveDocumentHasASTAttached() { const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses; IndexedTopDUContext indexed; ClangParsingEnvironment lastEnv; { TestFile file(QStringLiteral("int main() {}\n"), QStringLiteral("cpp")); auto astFeatures = static_cast(features | TopDUContext::AST); file.parse(astFeatures); file.setKeepDUChainData(true); QVERIFY(file.waitForParsed()); DUChainWriteLocker lock; auto top = file.topContext(); QVERIFY(top); auto sessionData = ParseSessionData::Ptr(dynamic_cast(top->ast().data())); lock.unlock(); ParseSession session(sessionData); lock.lock(); QVERIFY(session.data()); QVERIFY(top); QVERIFY(top->ast()); indexed = top->indexed(); } DUChain::self()->storeToDisk(); { DUChainWriteLocker lock; QVERIFY(!DUChain::self()->isInMemory(indexed.index())); QVERIFY(indexed.data()); } QUrl url; { DUChainReadLocker lock; auto ctx = indexed.data(); QVERIFY(ctx); QVERIFY(!ctx->ast()); url = ctx->url().toUrl(); } QVERIFY(!QFileInfo::exists(url.toLocalFile())); QFile file(url.toLocalFile()); file.open(QIODevice::WriteOnly); Q_ASSERT(file.isOpen()); auto document = ICore::self()->documentController()->openDocument(url); QVERIFY(document); ICore::self()->documentController()->activateDocument(document); QApplication::processEvents(); ICore::self()->languageController()->backgroundParser()->parseDocuments(); QThread::sleep(1); document->close(KDevelop::IDocument::Discard); { DUChainReadLocker lock; auto ctx = indexed.data(); QVERIFY(ctx); QVERIFY(ctx->ast()); } DUChainWriteLocker lock; DUChain::self()->removeDocumentChain(indexed.data()); } void TestDUChain::testActiveDocumentsGetBestPriority() { // note: this test would make more sense in kdevplatform, but we don't have a language plugin available there // (required for background parsing) // TODO: Create a fake-language plugin in kdevplatform for testing purposes, use that. TestFile file1(QStringLiteral("int main() {}\n"), QStringLiteral("cpp")); TestFile file2(QStringLiteral("int main() {}\n"), QStringLiteral("cpp")); TestFile file3(QStringLiteral("int main() {}\n"), QStringLiteral("cpp")); DUChain::self()->storeToDisk(); auto backgroundParser = ICore::self()->languageController()->backgroundParser(); QVERIFY(!backgroundParser->isQueued(file1.url())); auto documentController = ICore::self()->documentController(); // open first document (no activation) auto doc = documentController->openDocument(file1.url().toUrl(), KTextEditor::Range::invalid(), {IDocumentController::DoNotActivate}); QVERIFY(doc); QVERIFY(backgroundParser->isQueued(file1.url())); QCOMPARE(backgroundParser->priorityForDocument(file1.url()), (int)BackgroundParser::NormalPriority); // open second document, activate doc = documentController->openDocument(file2.url().toUrl()); QVERIFY(doc); QVERIFY(backgroundParser->isQueued(file2.url())); QCOMPARE(backgroundParser->priorityForDocument(file2.url()), (int)BackgroundParser::BestPriority); // open third document, activate, too doc = documentController->openDocument(file3.url().toUrl()); QVERIFY(doc); QVERIFY(backgroundParser->isQueued(file3.url())); QCOMPARE(backgroundParser->priorityForDocument(file3.url()), (int)BackgroundParser::BestPriority); } void TestDUChain::testSystemIncludes() { ClangParsingEnvironment env; Path::List projectIncludes = { Path("/projects/1"), Path("/projects/1/sub"), Path("/projects/2"), Path("/projects/2/sub") }; env.addIncludes(projectIncludes); auto includes = env.includes(); // no project paths set, so everything is considered a system include QCOMPARE(includes.system, projectIncludes); QVERIFY(includes.project.isEmpty()); Path::List systemIncludes = { Path("/sys"), Path("/sys/sub") }; env.addIncludes(systemIncludes); includes = env.includes(); QCOMPARE(includes.system, projectIncludes + systemIncludes); QVERIFY(includes.project.isEmpty()); Path::List projects = { Path("/projects/1"), Path("/projects/2") }; env.setProjectPaths(projects); // now the list should be properly separated QCOMPARE(env.projectPaths(), projects); includes = env.includes(); QCOMPARE(includes.system, systemIncludes); QCOMPARE(includes.project, projectIncludes); } void TestDUChain::testReparseWithAllDeclarationsContextsAndUses() { TestFile file(QStringLiteral("int foo() { return 0; } int main() { return foo(); }"), QStringLiteral("cpp")); file.parse(TopDUContext::VisibleDeclarationsAndContexts); QVERIFY(file.waitForParsed(1000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto dec = file.topContext()->localDeclarations().at(0); QEXPECT_FAIL("", "Skipping of function bodies is disabled for now", Continue); QVERIFY(dec->uses().isEmpty()); } file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(500)); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto mainDecl = file.topContext()->localDeclarations()[1]; QVERIFY(mainDecl->uses().isEmpty()); auto foo = file.topContext()->localDeclarations().first(); QCOMPARE(foo->uses().size(), 1); } } void TestDUChain::testReparseOnDocumentActivated() { TestFile file(QStringLiteral("int foo() { return 0; } int main() { return foo(); }"), QStringLiteral("cpp")); file.parse(TopDUContext::VisibleDeclarationsAndContexts); QVERIFY(file.waitForParsed(1000)); { DUChainReadLocker lock; auto ctx = file.topContext(); QVERIFY(ctx); QCOMPARE(ctx->childContexts().size(), 2); QCOMPARE(ctx->localDeclarations().size(), 2); auto dec = ctx->localDeclarations().at(0); QEXPECT_FAIL("", "Skipping of function bodies was disabled for now", Continue); QVERIFY(dec->uses().isEmpty()); QVERIFY(!ctx->ast()); } auto backgroundParser = ICore::self()->languageController()->backgroundParser(); QVERIFY(!backgroundParser->isQueued(file.url())); auto doc = ICore::self()->documentController()->openDocument(file.url().toUrl()); QVERIFY(doc); QVERIFY(backgroundParser->isQueued(file.url())); QSignalSpy spy(backgroundParser, &BackgroundParser::parseJobFinished); spy.wait(); doc->close(KDevelop::IDocument::Discard); { DUChainReadLocker lock; auto ctx = file.topContext(); QCOMPARE(ctx->features() & TopDUContext::AllDeclarationsContextsAndUses, static_cast(TopDUContext::AllDeclarationsContextsAndUses)); QVERIFY(ctx->topContext()->ast()); } } void TestDUChain::testReparseInclude() { TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h")); TestFile impl("#include \"" + header.url().str() + "\"\n" "int main() { return foo(); }", QStringLiteral("cpp"), &header); // Use TopDUContext::AST to imitate that document is opened in the editor, so that ClangParseJob can store translation unit, that'll be used for reparsing. impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsAndContexts|TopDUContext::AST)); QVERIFY(impl.waitForParsed(5000)); { DUChainReadLocker lock; auto implCtx = impl.topContext(); QVERIFY(implCtx); QCOMPARE(implCtx->importedParentContexts().size(), 1); } impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST)); QVERIFY(impl.waitForParsed(5000)); DUChainReadLocker lock; auto implCtx = impl.topContext(); QVERIFY(implCtx); QCOMPARE(implCtx->localDeclarations().size(), 1); QCOMPARE(implCtx->importedParentContexts().size(), 1); auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); QVERIFY(!headerCtx->parsingEnvironmentFile()->needsUpdate()); QCOMPARE(headerCtx->localDeclarations().size(), 1); QVERIFY(implCtx->imports(headerCtx, CursorInRevision(0, 10))); Declaration* foo = headerCtx->localDeclarations().first(); QCOMPARE(foo->uses().size(), 1); QCOMPARE(foo->uses().begin().key(), impl.url()); QCOMPARE(foo->uses().begin()->size(), 1); QCOMPARE(foo->uses().begin()->first(), RangeInRevision(1, 20, 1, 23)); QCOMPARE(DUChain::self()->allEnvironmentFiles(header.url()).size(), 1); QCOMPARE(DUChain::self()->allEnvironmentFiles(impl.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(header.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(impl.url()).size(), 1); } void TestDUChain::testReparseChangeEnvironment() { TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h")); TestFile impl("#include \"" + header.url().str() + "\"\n" "int main() { return foo(); }", QStringLiteral("cpp"), &header); uint hashes[3] = {0, 0, 0}; for (int i = 0; i < 3; ++i) { impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(impl.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(impl.topContext()); auto env = dynamic_cast(impl.topContext()->parsingEnvironmentFile().data()); QVERIFY(env); QCOMPARE(env->environmentQuality(), ClangParsingEnvironment::Source); hashes[i] = env->environmentHash(); QVERIFY(hashes[i]); // we should never end up with multiple env files or chains in memory for these files QCOMPARE(DUChain::self()->allEnvironmentFiles(impl.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(impl.url()).size(), 1); QCOMPARE(DUChain::self()->allEnvironmentFiles(header.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(header.url()).size(), 1); } // in every run, we expect the environment to have changed for (int j = 0; j < i; ++j) { QVERIFY(hashes[i] != hashes[j]); } if (i == 0) { // 1) change defines m_provider->defines.insert(QStringLiteral("foooooooo"), QStringLiteral("baaar!")); } else if (i == 1) { // 2) change includes m_provider->includes.append(Path(QStringLiteral("/foo/bar/asdf/lalala"))); } // 3) stop } } void TestDUChain::testMacroDependentHeader() { TestFile header(QStringLiteral("struct MY_CLASS { struct Q{Q(); int m;}; int m; };\n"), QStringLiteral("h")); TestFile impl("#define MY_CLASS A\n" "#include \"" + header.url().str() + "\"\n" "#undef MY_CLASS\n" "#define MY_CLASS B\n" "#include \"" + header.url().str() + "\"\n" "#undef MY_CLASS\n" "A a;\n" "const A::Q aq;\n" "B b;\n" "const B::Q bq;\n" "int am = a.m;\n" "int aqm = aq.m;\n" "int bm = b.m;\n" "int bqm = bq.m;\n" , QStringLiteral("cpp"), &header); impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(impl.waitForParsed(500000)); DUChainReadLocker lock; TopDUContext* top = impl.topContext().data(); QVERIFY(top); QCOMPARE(top->localDeclarations().size(), 10); // 2x macro, then a, aq, b, bq QCOMPARE(top->importedParentContexts().size(), 1); AbstractType::Ptr type = top->localDeclarations()[2]->abstractType(); auto* sType = dynamic_cast(type.data()); QVERIFY(sType); QCOMPARE(sType->toString(), QString("A")); Declaration* decl = sType->declaration(top); QVERIFY(decl); AbstractType::Ptr type2 = top->localDeclarations()[4]->abstractType(); auto* sType2 = dynamic_cast(type2.data()); QVERIFY(sType2); QCOMPARE(sType2->toString(), QString("B")); Declaration* decl2 = sType2->declaration(top); QVERIFY(decl2); TopDUContext* top2 = dynamic_cast(top->importedParentContexts()[0].context(top)); QVERIFY(top2); QCOMPARE(top2->localDeclarations().size(), 2); QCOMPARE(top2->localDeclarations()[0], decl); QCOMPARE(top2->localDeclarations()[1], decl2); qDebug() << "DECL RANGE:" << top2->localDeclarations()[0]->range().castToSimpleRange(); qDebug() << "CTX RANGE:" << top2->localDeclarations()[0]->internalContext()->range().castToSimpleRange(); // validate uses: QCOMPARE(top->usesCount(), 14); QCOMPARE(top->uses()[0].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("A")); QCOMPARE(top->uses()[1].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("A")); QCOMPARE(top->uses()[2].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("A::Q")); QCOMPARE(top->uses()[3].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("B")); QCOMPARE(top->uses()[4].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("B")); QCOMPARE(top->uses()[5].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("B::Q")); QCOMPARE(top->uses()[6].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("a")); QCOMPARE(top->uses()[7].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("A::m")); QCOMPARE(top->uses()[8].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("aq")); QCOMPARE(top->uses()[9].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("A::Q::m")); QCOMPARE(top->uses()[10].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("b")); QCOMPARE(top->uses()[11].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("B::m")); QCOMPARE(top->uses()[12].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("bq")); QCOMPARE(top->uses()[13].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier("B::Q::m")); } void TestDUChain::testHeaderParsingOrder1() { TestFile header(QStringLiteral("typedef const A B;\n"), QStringLiteral("h")); TestFile impl("template class A{};\n" "#include \"" + header.url().str() + "\"\n" "B c;", QStringLiteral("cpp"), &header); impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(impl.waitForParsed(500000)); DUChainReadLocker lock; TopDUContext* top = impl.topContext().data(); QVERIFY(top); QCOMPARE(top->localDeclarations().size(), 2); QCOMPARE(top->importedParentContexts().size(), 1); AbstractType::Ptr type = top->localDeclarations()[1]->abstractType(); auto* aType = dynamic_cast(type.data()); QVERIFY(aType); AbstractType::Ptr targetType = aType->type(); QVERIFY(targetType); auto *idType = dynamic_cast(targetType.data()); QVERIFY(idType); // this declaration could be resolved, because it was created with an // indirect DeclarationId that is resolved from the perspective of 'top' Declaration* decl = idType->declaration(top); // NOTE: the decl. doesn't know (yet) about the template insantiation QVERIFY(decl); QCOMPARE(decl, top->localDeclarations()[0]); // now ensure that a use was build for 'A' in header1 TopDUContext* top2 = dynamic_cast(top->importedParentContexts()[0].context(top)); QVERIFY(top2); QEXPECT_FAIL("", "the use could not be created because the corresponding declaration didn't exist yet", Continue); QCOMPARE(top2->usesCount(), 1); // Declaration* decl2 = top2->uses()[0].usedDeclaration(top2); // QVERIFY(decl2); // QCOMPARE(decl, decl2); } void TestDUChain::testHeaderParsingOrder2() { TestFile header(QStringLiteral("template class A{};\n"), QStringLiteral("h")); TestFile header2(QStringLiteral("typedef const A B;\n"), QStringLiteral("h")); TestFile impl("#include \"" + header.url().str() + "\"\n" "#include \"" + header2.url().str() + "\"\n" "B c;", QStringLiteral("cpp"), &header); impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(impl.waitForParsed(500000)); DUChainReadLocker lock; TopDUContext* top = impl.topContext().data(); QVERIFY(top); QCOMPARE(top->localDeclarations().size(), 1); QCOMPARE(top->importedParentContexts().size(), 2); AbstractType::Ptr type = top->localDeclarations()[0]->abstractType(); auto* aType = dynamic_cast(type.data()); QVERIFY(aType); AbstractType::Ptr targetType = aType->type(); QVERIFY(targetType); auto *idType = dynamic_cast(targetType.data()); QVERIFY(idType); Declaration* decl = idType->declaration(top); // NOTE: the decl. doesn't know (yet) about the template insantiation QVERIFY(decl); // now ensure that a use was build for 'A' in header2 TopDUContext* top2 = dynamic_cast(top->importedParentContexts()[1].context(top)); QVERIFY(top2); QCOMPARE(top2->usesCount(), 1); Declaration* decl2 = top2->uses()[0].usedDeclaration(top2); QCOMPARE(decl, decl2); } void TestDUChain::testMacrosRanges() { TestFile file(QStringLiteral("#define FUNC_MACROS(x) struct str##x{};\nFUNC_MACROS(x);"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto macroDefinition = file.topContext()->localDeclarations()[0]; QVERIFY(macroDefinition); QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,19)); auto structDeclaration = file.topContext()->localDeclarations()[1]; QVERIFY(structDeclaration); QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0)); QCOMPARE(macroDefinition->uses().size(), 1); QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,11)); } void TestDUChain::testMacroUses() { TestFile file(QStringLiteral("#define USER(x) x\n#define USED\nUSER(USED)"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto macroDefinition1 = file.topContext()->localDeclarations()[0]; auto macroDefinition2 = file.topContext()->localDeclarations()[1]; QCOMPARE(macroDefinition1->uses().size(), 1); QCOMPARE(macroDefinition1->uses().begin()->first(), RangeInRevision(2,0,2,4)); #if CINDEX_VERSION_MINOR < 32 QEXPECT_FAIL("", "This appears to be a clang bug, the AST doesn't contain the macro use", Continue); #endif QCOMPARE(macroDefinition2->uses().size(), 1); if (macroDefinition2->uses().size()) { QCOMPARE(macroDefinition2->uses().begin()->first(), RangeInRevision(2,5,2,9)); } } void TestDUChain::testMultiLineMacroRanges() { TestFile file(QStringLiteral("#define FUNC_MACROS(x) struct str##x{};\nFUNC_MACROS(x\n);"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto macroDefinition = file.topContext()->localDeclarations()[0]; QVERIFY(macroDefinition); QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,19)); auto structDeclaration = file.topContext()->localDeclarations()[1]; QVERIFY(structDeclaration); QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0)); QCOMPARE(macroDefinition->uses().size(), 1); QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,11)); } void TestDUChain::testNestedMacroRanges() { TestFile file(QStringLiteral("#define INNER int var; var = 0;\n#define MACRO() INNER\nint main(){MACRO(\n);}"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 3); auto main = file.topContext()->localDeclarations()[2]; QVERIFY(main); auto mainCtx = main->internalContext()->childContexts().first(); QVERIFY(mainCtx); QCOMPARE(mainCtx->localDeclarations().size(), 1); auto var = mainCtx->localDeclarations().first(); QVERIFY(var); QCOMPARE(var->range(), RangeInRevision(2,11,2,11)); QCOMPARE(var->uses().size(), 1); QCOMPARE(var->uses().begin()->first(), RangeInRevision(2,11,2,11)); } void TestDUChain::testNestedImports() { TestFile B(QStringLiteral("#pragma once\nint B();\n"), QStringLiteral("h")); TestFile C("#pragma once\n#include \"" + B.url().str() + "\"\nint C();\n", QStringLiteral("h")); TestFile A("#include \"" + B.url().str() + "\"\n" + "#include \"" + C.url().str() + "\"\nint A();\n", QStringLiteral("cpp")); A.parse(); QVERIFY(A.waitForParsed(5000)); DUChainReadLocker lock; auto BCtx = DUChain::self()->chainForDocument(B.url().toUrl()); QVERIFY(BCtx); QVERIFY(BCtx->importedParentContexts().isEmpty()); auto CCtx = DUChain::self()->chainForDocument(C.url().toUrl()); QVERIFY(CCtx); QCOMPARE(CCtx->importedParentContexts().size(), 1); QVERIFY(CCtx->imports(BCtx, CursorInRevision(1, 10))); auto ACtx = A.topContext(); QVERIFY(ACtx); QCOMPARE(ACtx->importedParentContexts().size(), 2); QVERIFY(ACtx->imports(BCtx, CursorInRevision(0, 10))); QVERIFY(ACtx->imports(CCtx, CursorInRevision(1, 10))); } void TestDUChain::testEnvironmentWithDifferentOrderOfElements() { TestFile file(QStringLiteral("int main();\n"), QStringLiteral("cpp")); m_provider->includes.clear(); m_provider->includes.append(Path(QStringLiteral("/path1"))); m_provider->includes.append(Path(QStringLiteral("/path2"))); m_provider->defines.clear(); m_provider->defines.insert(QStringLiteral("key1"), QStringLiteral("value1")); m_provider->defines.insert(QStringLiteral("key2"), QStringLiteral("value2")); m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3")); uint previousHash = 0; for (int i: {0, 1, 2, 3}) { file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(file.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); auto env = dynamic_cast(file.topContext()->parsingEnvironmentFile().data()); QVERIFY(env); QCOMPARE(env->environmentQuality(), ClangParsingEnvironment::Source); if (previousHash) { if (i == 3) { QVERIFY(previousHash != env->environmentHash()); } else { QCOMPARE(previousHash, env->environmentHash()); } } previousHash = env->environmentHash(); QVERIFY(previousHash); } if (i == 0) { //Change order of defines. Hash of the environment should stay the same. m_provider->defines.clear(); m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3")); m_provider->defines.insert(QStringLiteral("key1"), QStringLiteral("value1")); m_provider->defines.insert(QStringLiteral("key2"), QStringLiteral("value2")); } else if (i == 1) { //Add the same macros twice. Hash of the environment should stay the same. m_provider->defines.clear(); m_provider->defines.insert(QStringLiteral("key2"), QStringLiteral("value2")); m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3")); m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3")); m_provider->defines.insert(QStringLiteral("key1"), QStringLiteral("value1")); } else if (i == 2) { //OTOH order of includes should change hash of the environment. m_provider->includes.clear(); m_provider->includes.append(Path(QStringLiteral("/path2"))); m_provider->includes.append(Path(QStringLiteral("/path1"))); } } } void TestDUChain::testReparseMacro() { TestFile file(QStringLiteral("#define DECLARE(a) typedef struct a##_ {} *a;\nDECLARE(D);\nD d;"), QStringLiteral("cpp")); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST)); QVERIFY(file.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 5); auto macroDefinition = file.topContext()->localDeclarations()[0]; QVERIFY(macroDefinition); QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,15)); QCOMPARE(macroDefinition->uses().size(), 1); QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,7)); auto structDeclaration = file.topContext()->localDeclarations()[1]; QVERIFY(structDeclaration); QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0)); auto structTypedef = file.topContext()->localDeclarations()[3]; QVERIFY(structTypedef); QCOMPARE(structTypedef->range(), RangeInRevision(1,8,1,9)); QCOMPARE(structTypedef->uses().size(), 1); QCOMPARE(structTypedef->uses().begin()->first(), RangeInRevision(2,0,2,1)); } void TestDUChain::testGotoStatement() { TestFile file(QStringLiteral("int main() {\ngoto label;\ngoto label;\nlabel: return 0;}"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 1); auto main = file.topContext()->localDeclarations()[0]; QVERIFY(main); auto mainCtx = main->internalContext()->childContexts().first(); QVERIFY(mainCtx); QCOMPARE(mainCtx->localDeclarations().size(), 1); auto label = mainCtx->localDeclarations().first(); QVERIFY(label); QCOMPARE(label->range(), RangeInRevision(3,0,3,5)); QCOMPARE(label->uses().size(), 1); QCOMPARE(label->uses().begin()->first(), RangeInRevision(1,5,1,10)); QCOMPARE(label->uses().begin()->last(), RangeInRevision(2,5,2,10)); } void TestDUChain::testRangesOfOperatorsInsideMacro() { TestFile file(QStringLiteral("class Test{public: Test& operator++(int);};\n#define MACRO(var) var++;\nint main(){\nTest tst; MACRO(tst)}"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 3); auto testClass = file.topContext()->localDeclarations()[0]; QVERIFY(testClass); auto operatorPlusPlus = testClass->internalContext()->localDeclarations().first(); QVERIFY(operatorPlusPlus); QCOMPARE(operatorPlusPlus->uses().size(), 1); QCOMPARE(operatorPlusPlus->uses().begin()->first(), RangeInRevision(3,10,3,10)); } void TestDUChain::testUsesCreatedForDeclarations() { auto code = R"(template void functionTemplate(T); template void functionTemplate(U) {} namespace NS { class Class{}; } using NS::Class; Class function(); NS::Class function() { return {}; } int main () { functionTemplate(int()); function(); } )"; TestFile file(code, QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; QVERIFY(file.topContext()); auto functionTemplate = file.topContext()->findDeclarations(QualifiedIdentifier(QStringLiteral("functionTemplate"))); QVERIFY(!functionTemplate.isEmpty()); auto functionTemplateDeclaration = DUChainUtils::declarationForDefinition(functionTemplate.first()); QVERIFY(!functionTemplateDeclaration->isDefinition()); #if CINDEX_VERSION_MINOR < 29 QEXPECT_FAIL("", "No API in LibClang to determine function template type", Continue); #endif QCOMPARE(functionTemplateDeclaration->uses().count(), 1); auto function = file.topContext()->findDeclarations(QualifiedIdentifier(QStringLiteral("function"))); QVERIFY(!function.isEmpty()); auto functionDeclaration = DUChainUtils::declarationForDefinition(function.first()); QVERIFY(!functionDeclaration->isDefinition()); QCOMPARE(functionDeclaration->uses().count(), 1); } void TestDUChain::testReparseIncludeGuard() { TestFile header(QStringLiteral("#ifndef GUARD\n#define GUARD\nint something;\n#endif\n"), QStringLiteral("h")); TestFile impl("#include \"" + header.url().str() + "\"\n", QStringLiteral("cpp"), &header); QVERIFY(impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST ))); { DUChainReadLocker lock; QCOMPARE(static_cast(impl.topContext()-> importedParentContexts().first().context(impl.topContext()))->problems().size(), 0); } QVERIFY(impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive))); { DUChainReadLocker lock; QCOMPARE(static_cast(impl.topContext()-> importedParentContexts().first().context(impl.topContext()))->problems().size(), 0); } } void TestDUChain::testExternC() { auto code = R"(extern "C" { void foo(); })"; TestFile file(code, QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); QVERIFY(!top->findDeclarations(QualifiedIdentifier("foo")).isEmpty()); } void TestDUChain::testReparseUnchanged_data() { QTest::addColumn("headerCode"); QTest::addColumn("implCode"); QTest::newRow("include-guards") << R"( #ifndef GUARD #define GUARD int something; #endif )" << R"( #include "%1" )"; QTest::newRow("template-default-parameters") << R"( #ifndef TEST_H #define TEST_H template class dummy; template class dummy { int field[T]; }; #endif )" << R"( #include "%1" int main(int, char **) { dummy<> x; (void)x; } )"; } void TestDUChain::testReparseUnchanged() { QFETCH(QString, headerCode); QFETCH(QString, implCode); TestFile header(headerCode, QStringLiteral("h")); TestFile impl(implCode.arg(header.url().str()), QStringLiteral("cpp"), &header); auto checkProblems = [&] (bool reparsed) { DUChainReadLocker lock; auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); QVERIFY(headerCtx->problems().isEmpty()); auto implCtx = DUChain::self()->chainForDocument(impl.url()); QVERIFY(implCtx); if (reparsed && CINDEX_VERSION_MINOR > 29 && CINDEX_VERSION_MINOR < 33) { QEXPECT_FAIL("template-default-parameters", "the precompiled preamble messes the default template parameters up in clang 3.7", Continue); } QVERIFY(implCtx->problems().isEmpty()); }; impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST )); checkProblems(false); impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); checkProblems(true); } void TestDUChain::testTypeAliasTemplate() { TestFile file(QStringLiteral("template using Alias = T; using Foo = Alias;"), QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto templateAlias = file.topContext()->localDeclarations().first(); QVERIFY(templateAlias); #if CINDEX_VERSION_MINOR < 31 QEXPECT_FAIL("", "TypeAliasTemplate is not exposed via LibClang", Abort); #endif QVERIFY(templateAlias->isTypeAlias()); QVERIFY(templateAlias->abstractType()); QCOMPARE(templateAlias->abstractType()->toString(), QStringLiteral("Alias")); QCOMPARE(templateAlias->uses().size(), 1); QCOMPARE(templateAlias->uses().first().size(), 1); QCOMPARE(templateAlias->uses().first().first(), RangeInRevision(0, 51, 0, 56)); } void TestDUChain::testDeclarationsInsideMacroExpansion() { TestFile header(QStringLiteral("#define DECLARE(a) typedef struct a##__ {int var;} *a\nDECLARE(D);\n"), QStringLiteral("h")); TestFile file("#include \"" + header.url().str() + "\"\nint main(){\nD d; d->var;}\n", QStringLiteral("cpp")); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST)); QVERIFY(file.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 1); auto context = file.topContext()->childContexts().first()->childContexts().first(); QVERIFY(context); QCOMPARE(context->localDeclarations().size(), 1); QCOMPARE(context->usesCount(), 3); QCOMPARE(context->uses()[0].m_range, RangeInRevision({2, 0}, {2, 1})); QCOMPARE(context->uses()[1].m_range, RangeInRevision({2, 5}, {2, 6})); QCOMPARE(context->uses()[2].m_range, RangeInRevision({2, 8}, {2, 11})); } // see also: https://bugs.kde.org/show_bug.cgi?id=368067 void TestDUChain::testForwardTemplateTypeParameterContext() { TestFile file(QStringLiteral(R"( template class Foo; class MatchingName { void bar(); }; void MatchingName::bar() { } )"), QStringLiteral("cpp")); file.parse(); QVERIFY(file.waitForParsed(500)); DUChainReadLocker lock; const auto top = file.topContext(); QVERIFY(top); DUChainDumper dumper(DUChainDumper::Features(DUChainDumper::DumpContext | DUChainDumper::DumpProblems)); dumper.dump(top); auto declarations = top->localDeclarations(); QCOMPARE(declarations.size(), 2); } // see also: https://bugs.kde.org/show_bug.cgi?id=368460 void TestDUChain::testTemplateFunctionParameterName() { TestFile file(QStringLiteral(R"( template void foo(int name); void bar(int name); )"), QStringLiteral("cpp")); file.parse(); QVERIFY(file.waitForParsed(500)); DUChainReadLocker lock; const auto top = file.topContext(); QVERIFY(top); DUChainDumper dumper(DUChainDumper::Features(DUChainDumper::DumpContext | DUChainDumper::DumpProblems)); dumper.dump(top); - auto declarations = top->localDeclarations(); + const auto declarations = top->localDeclarations(); QCOMPARE(declarations.size(), 2); for (auto decl : declarations) { auto ctx = DUChainUtils::argumentContext(decl); QVERIFY(ctx); auto args = ctx->localDeclarations(); if (decl == declarations.first()) QEXPECT_FAIL("", "We get two declarations, for both template and args :(", Continue); QCOMPARE(args.size(), 1); if (decl == declarations.first()) QEXPECT_FAIL("", "see above, this then triggers T T here", Continue); QCOMPARE(args.first()->toString(), QStringLiteral("int name")); } } static bool containsErrors(const QList& problems) { auto it = std::find_if(problems.begin(), problems.end(), [] (const Problem::Ptr& problem) { return problem->severity() == Problem::Error; }); return it != problems.end(); } static bool expectedXmmintrinErrors(const QList& problems) { for (const auto& problem : problems) { if (problem->severity() == Problem::Error && !problem->description().contains(QLatin1String("Cannot initialize a parameter of type"))) { return false; } } return true; } static void verifyNoErrors(TopDUContext* top, QSet& checked) { const auto problems = top->problems(); if (containsErrors(problems)) { qDebug() << top->url() << top->problems(); if (top->url().str().endsWith(QLatin1String("xmmintrin.h")) && expectedXmmintrinErrors(problems)) { QEXPECT_FAIL("", "there are still some errors in xmmintrin.h b/c some clang provided intrinsincs are more strict than the GCC ones.", Continue); QVERIFY(false); } else { QFAIL("parse error detected"); } } const auto imports = top->importedParentContexts(); for (const auto& import : imports) { auto ctx = import.context(top); QVERIFY(ctx); auto importedTop = ctx->topContext(); if (checked.contains(importedTop)) { continue; } checked.insert(importedTop); verifyNoErrors(importedTop, checked); } } void TestDUChain::testFriendDeclaration() { TestFile file(QStringLiteral(R"( struct FriendFoo { friend class FriendBar; }; class FriendBar{}; FriendBar friendBar; )"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(1000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 3); auto friendBar = file.topContext()->localDeclarations()[1]; if (CINDEX_VERSION_MINOR < 37) { QEXPECT_FAIL("", "Your clang version is too old", Abort); } QCOMPARE(friendBar->uses().size(), 1); QCOMPARE(friendBar->uses().begin()->first(), RangeInRevision(3,25,3,34)); QCOMPARE(friendBar->uses().begin()->last(), RangeInRevision(8,8,8,17)); } } void TestDUChain::testVariadicTemplateArguments() { TestFile file(QStringLiteral(R"( template class VariadicTemplate {}; VariadicTemplate variadic; )"), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(1000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto decl = file.topContext()->localDeclarations()[1]; QVERIFY(decl); if (CINDEX_VERSION_MINOR < 37) { QEXPECT_FAIL("", "Your clang version is too old", Abort); } QCOMPARE(decl->toString(), QStringLiteral("VariadicTemplate< int, double, bool > variadic")); QVERIFY(decl->abstractType()); QCOMPARE(decl->abstractType()->toString(), QStringLiteral("VariadicTemplate< int, double, bool >")); } } void TestDUChain::testProblemRequestedHere() { auto headerCode = QStringLiteral(R"( #pragma once template T AddObjects(const T& a, const T& b) { return a + b; } )"); TestFile header(headerCode, QStringLiteral("h")); QString sourceCode = QStringLiteral(R"( #include "%1" struct A {}; int main() { A a, b; AddObjects(a, b); return 0; } )").arg(header.url().str()); TestFile impl(sourceCode, QStringLiteral("cpp"), &header); QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses)); DUChainReadLocker lock; auto top = impl.topContext(); QVERIFY(top); auto* headerCtx = dynamic_cast(top->importedParentContexts().first().context(top)); QVERIFY(headerCtx); QCOMPARE(headerCtx->url(), header.url()); QCOMPARE(headerCtx->problems().count(), 1); QCOMPARE(headerCtx->localDeclarations().count(), 1); // Verify that the problem is reported for the source file. QCOMPARE(top->problems().count(), 1); QCOMPARE(top->localDeclarations().count(), 2); } void TestDUChain::testProblemRequestedHereSameFile() { auto sourceCode = QStringLiteral(R"( struct A {}; template T AddObjects(const T& a, const T& b) { return a + b; } int main() { A a, b; AddObjects(a, b); return 0; } )"); TestFile impl(sourceCode, QStringLiteral("cpp")); QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses)); DUChainReadLocker lock; auto top = impl.topContext(); QVERIFY(top); QCOMPARE(top->problems().count(), 2); } void TestDUChain::testProblemRequestedHereChain() { auto headerCode = QStringLiteral(R"( #pragma once template T AddObjects(const T& a, const T& b) { return a + b; } )"); TestFile header(headerCode, QStringLiteral("h")); QString sourceCode = QStringLiteral(R"( #include "%1" struct A {}; template T AddObjects2(const T& a, const T& b) { return AddObjects(a, b); } int main() { A a, b; AddObjects2(a, b); return 0; } )").arg(header.url().str()); TestFile impl(sourceCode, QStringLiteral("cpp"), &header); QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses)); DUChainReadLocker lock; auto top = impl.topContext(); QVERIFY(top); auto* headerCtx = dynamic_cast(top->importedParentContexts().first().context(top)); QVERIFY(headerCtx); QCOMPARE(headerCtx->url(), header.url()); QCOMPARE(headerCtx->problems().count(), 1); QCOMPARE(headerCtx->localDeclarations().count(), 1); // Verify that the problem is reported for the source file. QCOMPARE(top->problems().count(), 2); QCOMPARE(top->localDeclarations().count(), 3); } void TestDUChain::testGccCompatibility() { // TODO: make it easier to change the compiler provider for testing purposes QTemporaryDir dir; auto project = new TestProject(Path(dir.path()), this); auto definesAndIncludesConfig = project->projectConfiguration()->group("CustomDefinesAndIncludes"); auto pathConfig = definesAndIncludesConfig.group("ProjectPath0"); pathConfig.writeEntry("Path", "."); pathConfig.group("Compiler").writeEntry("Name", "GCC"); m_projectController->addProject(project); { // TODO: Also test in C mode. Currently it doesn't work (some intrinsics missing?) TestFile file(QStringLiteral(R"( #include int main() { return 0; } )"), QStringLiteral("cpp"), project, dir.path()); file.parse(); QVERIFY(file.waitForParsed(50000)); DUChainReadLocker lock; QSet checked; verifyNoErrors(file.topContext(), checked); } m_projectController->closeAllProjects(); } void TestDUChain::testLambda() { TestFile file(QStringLiteral("auto lambda = [](int p1, int p2, int p3) { int var1, var2; };"), QStringLiteral("cpp")); QVERIFY(file.parseAndWait()); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 1); QCOMPARE(file.topContext()->childContexts().size(), 1); auto lambdaContext = file.topContext()->childContexts().first(); QCOMPARE(lambdaContext->type(), DUContext::Function); QCOMPARE(lambdaContext->localDeclarations().size(), 3); QCOMPARE(lambdaContext->childContexts().size(), 1); QCOMPARE(lambdaContext->childContexts().first()->type(), DUContext::Other); QCOMPARE(lambdaContext->childContexts().first()->localDeclarations().size(), 2); } } void TestDUChain::testQtIntegration() { QTemporaryDir includeDir; { QDir dir(includeDir.path()); dir.mkdir(QStringLiteral("QtCore")); // create the file but don't put anything in it QFile header(includeDir.path() + "/QtCore/qobjectdefs.h"); QVERIFY(header.open(QIODevice::WriteOnly | QIODevice::Text)); } QTemporaryDir dir; auto project = new TestProject(Path(dir.path()), this); m_provider->defines.clear(); m_provider->includes = {Path(includeDir.path() + "/QtCore")}; m_projectController->addProject(project); { TestFile file(QStringLiteral(R"( #define slots #define signals #define Q_SLOTS #define Q_SIGNALS #include struct MyObject { public: void other1(); public slots: void slot1(); signals: void signal1(); private Q_SLOTS: void slot2(); Q_SIGNALS: void signal2(); public: void other2(); }; )"), QStringLiteral("cpp"), project, dir.path()); file.parse(); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); QVERIFY(top->problems().isEmpty()); const auto methods = top->childContexts().last()->localDeclarations(); QCOMPARE(methods.size(), 6); for (auto method : methods) { auto classFunction = dynamic_cast(method); QVERIFY(classFunction); auto id = classFunction->identifier().toString(); QCOMPARE(classFunction->isSignal(), id.startsWith(QLatin1String("signal"))); QCOMPARE(classFunction->isSlot(), id.startsWith(QLatin1String("slot"))); } } m_projectController->closeAllProjects(); } void TestDUChain::testHasInclude() { TestFile header(QStringLiteral(R"( #pragma once #if __has_include_next() // good #else #error broken c++11 setup (__has_include_next) #endif )"), QStringLiteral("h")); // NOTE: header is _not_ explicitly being parsed, instead the impl job does that TestFile file(QStringLiteral(R"( #if __has_include() // good #else #error broken c++11 setup (__has_include) #endif #include "%1" )").arg(header.url().str()), QStringLiteral("cpp")); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(1000)); { DUChainDumper dumper{DUChainDumper::DumpProblems}; DUChainReadLocker lock; QVERIFY(file.topContext()); dumper.dump(file.topContext()); QVERIFY(file.topContext()->problems().isEmpty()); auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); dumper.dump(headerCtx); QVERIFY(headerCtx->problems().count() <= 1); const auto& headerProblems = headerCtx->problems(); for (const auto& problem : headerProblems) { // ignore the following error: "#include_next with absolute path [-Winclude-next-absolute-path]" "" [ (2, 12) -> (2, 30) ] QVERIFY(problem->description().contains(QLatin1String("-Winclude-next-absolute-path"))); } } } void TestDUChain::testSameFunctionDefinition() { QString source(QStringLiteral(R"( #include #include void configure() { printf("do stuff\n"); } )")); QTemporaryDir dir; auto project = new TestProject(Path(dir.path()), this); m_projectController->addProject(project); TestFile file1(source, QStringLiteral("c"), project); TestFile file2(source, QStringLiteral("c"), project); TestFile file3(source, QStringLiteral("c"), project); file1.parse(TopDUContext::AllDeclarationsContextsAndUses); file2.parse(TopDUContext::AllDeclarationsContextsAndUses); file3.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file1.waitForParsed(1000)); QVERIFY(file2.waitForParsed(1000)); QVERIFY(file3.waitForParsed(1000)); auto checkFunctionDefinition = [] (TestFile & file) { DUChainReadLocker lock; QCOMPARE(file.topContext()->localDeclarations().count(), 1); auto configureFunc = file.topContext()->localDeclarations().first(); QCOMPARE(FunctionDefinition::definition(configureFunc), configureFunc); }; checkFunctionDefinition(file1); checkFunctionDefinition(file2); checkFunctionDefinition(file3); m_projectController->closeAllProjects(); } diff --git a/plugins/clangtidy/kdevclangtidy.json b/plugins/clangtidy/kdevclangtidy.json index 7473adcb3a..1f804b044a 100644 --- a/plugins/clangtidy/kdevclangtidy.json +++ b/plugins/clangtidy/kdevclangtidy.json @@ -1,111 +1,102 @@ { "KPlugin": { "Authors": [ { "Name": "Carlos Nihelton", - "Name[ca@valencia]": "Carlos Nihelton", "Name[ca]": "Carlos Nihelton", "Name[cs]": "Carlos Nihelton", "Name[de]": "Carlos Nihelton", - "Name[el]": "Carlos Nihelton", "Name[en_GB]": "Carlos Nihelton", "Name[es]": "Carlos Nihelton", "Name[et]": "Carlos Nihelton", "Name[fr]": "Carlos Nihelton", "Name[gl]": "Carlos Nihelton", "Name[it]": "Carlos Nihelton", "Name[nl]": "Carlos Nihelton", "Name[pl]": "Carlos Nihelton", "Name[pt]": "Carlos Nihelton", "Name[pt_BR]": "Carlos Nihelton", - "Name[ru]": "Carlos Nihelton", "Name[sk]": "Carlos Nihelton", "Name[sv]": "Carlos Nihelton", "Name[uk]": "Carlos Nihelton", "Name[x-test]": "xxCarlos Niheltonxx", "Name[zh_CN]": "Carlos Nihelton" }, { "Name": "Friedrich W. H. Kossebau", "Name[ca@valencia]": "Friedrich W. H. Kossebau", "Name[ca]": "Friedrich W. H. Kossebau", "Name[cs]": "Friedrich W. H. Kossebau", "Name[de]": "Friedrich W. H. Kossebau", - "Name[el]": "Friedrich W. H. Kossebau", "Name[en_GB]": "Friedrich W. H. Kossebau", "Name[es]": "Friedrich W. H. Kossebau", "Name[et]": "Friedrich W. H. Kossebau", "Name[fi]": "Friedrich W. H. Kossebau", "Name[fr]": "Friedrich W. H. Kossebau", "Name[gl]": "Friedrich W. H. Kossebau", "Name[it]": "Friedrich W. H. Kossebau", "Name[nl]": "Friedrich W. H. Kossebau", "Name[nn]": "Friedrich W.H. Kossebau", "Name[pl]": "Friedrich W. H. Kossebau", "Name[pt]": "Friedrich W. H. Kossebau", "Name[pt_BR]": "Friedrich W. H. Kossebau", "Name[ru]": "Friedrich W. H. Kossebau", "Name[sk]": "Friedrich W. H. Kossebau", "Name[sl]": "Friedrich W. H. Kossebau", "Name[sv]": "Friedrich W. H. Kossebau", "Name[tr]": "Friedrich W. H. Kossebau", "Name[uk]": "Friedrich W. H. Kossebau", "Name[x-test]": "xxFriedrich W. H. Kossebauxx", "Name[zh_CN]": "Friedrich W. H. Kossebau" } ], "Category": "Analyzers", "Description": "clang-tidy provides an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. https://clang.llvm.org/extra/clang-tidy/", - "Description[ca@valencia]": "El clang-tidy proporciona un marc de treball extensible per al diagnòstic i esmena d'errors de programació típics, com ara infraccions de l'estil, mal ús de la interfície o errors que es poden deduir mitjançant anàlisi estàtica. https://clang.llvm.org/extra/clang-tidy/", "Description[ca]": "El clang-tidy proporciona un marc de treball extensible per al diagnòstic i esmena d'errors de programació típics, com ara infraccions de l'estil, mal ús de la interfície o errors que es poden deduir mitjançant anàlisi estàtica. https://clang.llvm.org/extra/clang-tidy/", - "Description[el]": "Το clang-tidy παρέχει ένα επεκτάσιμο πλαίσιο για διάγνωση και διόρθωση τυπικών προγραμματιστικών σφαλμάτων, όπως παραβιάσεις στο στιλ, κακή χρήση της διεπαφής, ή σφάλματα που ανακύπτουν από τη στατική ανάλυση. https://clang.llvm.org/extra/clang-tidy/", "Description[en_GB]": "clang-tidy provides an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. https://clang.llvm.org/extra/clang-tidy/", "Description[es]": "Clang-Tidy proporciona una infraestructura extensible para diagnosticar y corregir errores típicos de programación, como violaciones de estilo, mal uso de la interfaz o fallos que se pueden deducir mediante análisis estáticos. https://clang.llvm.org/extra/clang-tidy/", "Description[et]": "clang-tidy pakub ulatuslikku raamistikku tüüpiliste programmeerimisvigade (nt stiilivead, liideste väärkasutus või vead, mida saab järeldada staatilise analüüsi põhjal) diagnoosimiseks ja parandamiseks. https://clang.llvm.org/extra/clang-tidy/", "Description[fr]": "clang-tidy offre une infrastructure extensible pour le diagnostic et la correction des erreurs de programmation fréquentes : violations de normes de code, mauvaise utilisation des interfaces ou bogues pouvant être identifiés grâce à l'analyse statique. https://clang.llvm.org/extra/clang-tidy/", "Description[gl]": "clang-tidy fornece unha infraestrutura extensíbel para diagnosticar e solucionar erros de programación típicos, como violacións das regras de estilo, ou fallos que poden deducirse mediante a análise sintáctica. https://clang.llvm.org/extra/clang-tidy/", "Description[it]": "clang-tidy fornisce un'infrastruttura estensibile per diagnosticare e correggere dei classici errori di programmazione, che includono violazioni di stile, uso incorretto delle interfacce o errori che possono essere dedotti tramite analisi statica. https://clang.llvm.org/extra/clang-tidy/", - "Description[nl]": "clang-tidy biedt een uitbreidbaar framework voor diagnose en repareren van typische programmeringsfouten, zoals ingaan tegen de stijl, misbruik van interface of bugs die afgeleid worden via statische analyse. https://clang.llvm.org/extra/clang-tidy/", + "Description[nl]": "clang-tidy biedt een uit te breiden framework voor diagnostiseren en repareren van typische programmeringsfouten, zoals zondigen tegen de stijl, interfacemisbruik of bugs die afgeleid kunnen worden via statische analyse. https://clang.llvm.org/extra/clang-tidy/", "Description[pl]": "clang-tidy dostarcza szkielet do diagnostyki i naprawiania powszechnych błędów programistycznych, takich jak łamanie zasad, nadużycia w interfejsie, lub błędy, które można wykryć poprzez analizę statyczną. https://clang.llvm.org/extra/clang-tidy/", - "Description[pt]": "O 'clang-tidy' oferece uma plataforma extensível para diagnosticar e corrigir erros de programação típicos, como as violações do estilo, mau uso das interfaces ou erros que possam ser deduzidos por análise estática. https://clang.llvm.org/extra/clang-tidy/", + "Description[pt]": "O 'clang-tidy' oferece uma plataforma extensível para diagnosticar e corrigir erros de programação típicos, como violações do estilo, mau uso de interfaces ou erros que possam ser deduzidos por análise estática. https://clang.llvm.org/extra/clang-tidy/", "Description[pt_BR]": "O 'clang-tidy' oferece uma plataforma extensível para diagnosticar e corrigir erros de programação típicos, como as violações do estilo, mau uso das interfaces ou erros que possam ser deduzidos por análise estática. https://clang.llvm.org/extra/clang-tidy/", "Description[sk]": "clang-tidy poskytuje rozsiahly framework na diagnózu a opravu typických programátorských chýb, ako porušenie štýlov, nesprávne použitie rozhrania alebo chyby, ktoré sa dajú dedukovať cez statickú analýzu. https://clang.llvm.org/extra/clang-tidy/", - "Description[sv]": "Clang-Tidy tillhandahåller ett utvidgningsbart ramverk för att diagnosticera och rätta typiska programmeringsfel, som stilöverträdelser, gränssnittsmissbruk eller fel som kan härledas via statisk analys. https://clang.llvm.org/extra/clang-tidy/", + "Description[sv]": "Clang-Tidy tillhandahåller ett utökningsbart ramverk för att diagnosticera och rätta typiska programmeringsfel, som stilöverträdelser, felanvändning av gränssnitt eller fel som kan detekteras via statisk analys. https://clang.llvm.org/extra/clang-tidy/", "Description[uk]": "clang-tidy — розширена оболонка діагностування та виправлення типових помилок у програмах, зокрема порушень стилю, помилкового використання інтерфейсу та вад, які можна виявити за допомогою статичного аналізу. https://clang.llvm.org/extra/clang-tidy/", "Description[x-test]": "xxclang-tidy provides an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. https://clang.llvm.org/extra/clang-tidy/xx", "Description[zh_CN]": "clang-tidy 提供了一个可扩展的框架,用于诊断和修复典型的编程错误,例如样式违规,接口滥用或可以通过静态分析推断出的错误。https://clang.llvm.org/extra/clang-tidy/", "Icon": "dialog-ok", "Id": "kdevclangtidy", "License": "GPL", "Name": "Clang-Tidy Support", - "Name[ca@valencia]": "Implementació del Clang-Tidy", "Name[ca]": "Implementació del Clang-Tidy", "Name[cs]": "Podpora Clang-Tidy", "Name[de]": "Unterstützung für Clang-Tidy", - "Name[el]": "Υποστήριξη clang-tidy", "Name[en_GB]": "Clang-Tidy Support", "Name[es]": "Implementación de Clang-Tidy", - "Name[et]": "Clang-Tidy toetus", "Name[fr]": "Prise en charge de Clang-Tidy", "Name[gl]": "Compatibilidade con Clang-Tidy", "Name[it]": "Supporto per Clang-Tidy", - "Name[nl]": "Ondersteuning van Clang-Tidy", + "Name[nl]": "Ondersteuning voor Clang-Tidy", "Name[pl]": "Obsługa Clang-Tidy", "Name[pt]": "Suporte para o Clang-Tidy", "Name[pt_BR]": "Suporte à Clang-Tidy", "Name[sk]": "Podpora Clang-Tidy", - "Name[sv]": "Clang-Tidy-stöd", + "Name[sv]": "Stöd för Clang-Tidy", "Name[uk]": "Підтримка Clang-Tidy", "Name[x-test]": "xxClang-Tidy Supportxx", "Name[zh_CN]": "Clang-Tidy 支持", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.3.3" }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IBuildSystemManager" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/classbrowser/kdevclassbrowser.json b/plugins/classbrowser/kdevclassbrowser.json index 6ff4011db9..72e5807b70 100644 --- a/plugins/classbrowser/kdevclassbrowser.json +++ b/plugins/classbrowser/kdevclassbrowser.json @@ -1,100 +1,86 @@ { "KPlugin": { "Authors": [ { "Name": "Hamish Rodda", "Name[ca@valencia]": "Hamish Rodda", "Name[ca]": "Hamish Rodda", "Name[cs]": "Hamish Rodda", "Name[de]": "Hamish Rodda", - "Name[el]": "Hamish Rodda", "Name[en_GB]": "Hamish Rodda", "Name[es]": "Hamish Rodda", "Name[et]": "Hamish Rodda", "Name[fr]": "Hamish Rodda", "Name[gl]": "Hamish Rodda", "Name[it]": "Hamish Rodda", "Name[nl]": "Hamish Rodda", "Name[nn]": "Hamish Rodda", "Name[pl]": "Hamish Rodda", "Name[pt]": "Hamish Rodda", "Name[pt_BR]": "Hamish Rodda", "Name[ru]": "Hamish Rodda", "Name[sk]": "Hamish Rodda", "Name[sl]": "Hamish Rodda", "Name[sv]": "Hamish Rodda", "Name[tr]": "Hamish Rodda", "Name[uk]": "Hamish Rodda", "Name[x-test]": "xxHamish Roddaxx", "Name[zh_CN]": "Hamish Rodda" } ], "Category": "Core", "Description": "This plugin provides a browsable model of the currently parsed classes and other items.", - "Description[ar]": "توفّر هذه الملحقة طرازًا قابلًا للتّصفّح للأصناف المحلّلة حاليًّا والعناصر الأخرى.", "Description[ca@valencia]": "Aquest connector proporciona un model navegable de les classes analitzades actualment i altres elements.", "Description[ca]": "Aquest connector proporciona un model navegable de les classes analitzades actualment i altres elements.", "Description[de]": "Dieses Modul bietet eine Liste der aktuell eingelesenen Klassen und anderer Elemente.", - "Description[el]": "Αυτό το πρόσθετο προσφέρει ένα μοντέλο περιήγησης των αναλυμένων κλάσεων και άλλων αντικειμένων.", "Description[en_GB]": "This plugin provides a browsable model of the currently parsed classes and other items.", "Description[es]": "Este complemento proporciona un modelo navegable de las clases actualmente analizadas y de otros elementos.", "Description[et]": "See plugin pakub parajasti parsitavate klasside ja teiste elementide sirvitavat mudelit.", "Description[fr]": "Ce module fournit un modèle navigable des classes actuellement analysées et d'autres éléments.", "Description[gl]": "Este complemento fornece un modelo navegábel das clases e outros elementos que estean procesados.", "Description[it]": "Questa estensione fornisce un modello navigabile delle classi attualmente analizzate e di altri elementi.", "Description[nl]": "Deze plugin levert een model om door te bladeren van de nu ontlede klasse's en andere items.", "Description[pl]": "Umożliwia przeglądanie stworzonego modelu klas i innych elementów.", "Description[pt]": "Este 'plugin' oferece uma visão navegável sobre as classes e outros itens que estejam a ser processados de momento.", "Description[pt_BR]": "Este plugin fornece um modelo navegável das classes atualmente analisadas e outros itens.", "Description[sk]": "Tento plugin poskytuje prehliadateľný model aktuálne analyzovanej triedy a iných položiek.", "Description[sl]": "Vstavek vgradi model trenutno razčlenjenih razredov in drugih predmetov, po katerih je mogoče brskati.", "Description[sv]": "Insticksprogrammet tillhandahåller en bläddringsbar modell av klasser och andra objekt som för närvarande har tolkats.", "Description[tr]": "Bu eklenti mevcut ayrıştırılmış sınıfların ve diğer öğelerin taranabilir bir modelini sağlar.", "Description[uk]": "За допомогою цього додатка можна створити придатну для перегляду модель класів та інших елементів, над якими ви працюєте.", "Description[x-test]": "xxThis plugin provides a browsable model of the currently parsed classes and other items.xx", "Description[zh_CN]": "此插件提供了一个当前已分析类和其它项目的可浏览化视窗模型。", - "Description[zh_TW]": "此外掛程式提供一個可瀏覽目前已分析的類別與其他項目的模組。", "Icon": "code-class", "Id": "kdevclassbrowser", "License": "GPL", "Name": "Class Browser", - "Name[ar]": "متصفّح الأصناف", - "Name[bg]": "Браузър за класове", - "Name[bs]": "Pregledač klasa", "Name[ca@valencia]": "Navegador de classes", "Name[ca]": "Navegador de classes", "Name[cs]": "Prohlížeč tříd", - "Name[da]": "Klassebrowser", "Name[de]": "Klassen-Browser", - "Name[el]": "Περιηγητής κλάσεων", "Name[en_GB]": "Class Browser", "Name[es]": "Navegador de clases", "Name[et]": "Klassibrauser", "Name[fr]": "Navigateur de classes", "Name[gl]": "Navegador de clases", - "Name[hu]": "Osztályböngésző", "Name[it]": "Navigatore delle classi", - "Name[kk]": "Класс шолғышы", "Name[nb]": "Klasseleser", - "Name[nds]": "Klassenkieker", "Name[nl]": "Klassen-browser", "Name[pl]": "Przeglądarka klas", "Name[pt]": "Navegador de Classes", "Name[pt_BR]": "Navegador de classes", "Name[ru]": "Дерево классов", "Name[sk]": "Prehliadač tried", "Name[sl]": "Brskalnik po razredih", "Name[sv]": "Klassbläddrare", "Name[tr]": "Sınıf Tarayıcı", - "Name[ug]": "تىپ كۆرگۈ", "Name[uk]": "Переглядач класів", "Name[x-test]": "xxClass Browserxx", "Name[zh_CN]": "类浏览器", - "Name[zh_TW]": "類別瀏覽器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/clazy/config/globalsettings.kcfg b/plugins/clazy/config/globalsettings.kcfg index b6be5eb12d..3320a01702 100644 --- a/plugins/clazy/config/globalsettings.kcfg +++ b/plugins/clazy/config/globalsettings.kcfg @@ -1,43 +1,43 @@ - - "jobparameters.h" + + JobGlobalParameters::defaultExecutablePath() JobGlobalParameters::defaultDocsPath() true true 2 true false diff --git a/plugins/clazy/config/projectconfigpage.ui b/plugins/clazy/config/projectconfigpage.ui index c557ae47a8..a99618b3f9 100644 --- a/plugins/clazy/config/projectconfigpage.ui +++ b/plugins/clazy/config/projectconfigpage.ui @@ -1,306 +1,306 @@ Clazy::ProjectConfigPage 0 0 723 396 0 0 0 0 0 0 0 Checks 1 0 Options Do not emit warnings for non-Qt files, or in other words, if -DQT_CORE_LIB is missing. Only Qt Disable checks not compatible with Qt 4. Qt4 compatible - For running clazy on Qt itself, optional, but honours specific guidelines. + For running clazy on Qt itself, optional, but honors specific guidelines. Qt developer For visiting implicit code like compiler generated constructors. None of the built-in checks benefit from this, but can be useful for custom checks. Visit implicit code Only emit warnings for the current file being compiled and ignore any includes. Useful for performance reasons. Ignore included files Header filter: Regular expression matching the names of the headers to output diagnostics from. Diagnostics from the main file of each translation unit are always displayed. true Enable all fixits Fixits will be applied to a separate file (for unit-test use only). No-inplace fixits Please backup your code before fixits applying. For better results also disable parallel checking, which can lead to multiple fixit applying for header files. false KMessageWidget::Warning Extra Parameters Compiler append: Additional parameters to append to the compiler command line. true Compiler prepend: Additional parameters to prepend to the compiler command line. true Additional parameters to clazy-standalone. true Extra parameters: dbError true false KMessageWidget::Error Clazy::CheckSetSelectionComboBox QComboBox
config/checksetselectioncombobox.h
Clazy::ChecksWidget QWidget
config/checkswidget.h
KMessageWidget QFrame
KMessageWidget
CommandLineWidget QWidget
config/commandlinewidget.h
1
diff --git a/plugins/clazy/kdevclazy.json b/plugins/clazy/kdevclazy.json index 6cfb459941..01c3956bad 100644 --- a/plugins/clazy/kdevclazy.json +++ b/plugins/clazy/kdevclazy.json @@ -1,87 +1,82 @@ { "KPlugin": { "Authors": [ { "Name": "Anton Anikin", "Name[ca@valencia]": "Anton Anikin", "Name[ca]": "Anton Anikin", "Name[cs]": "Anton Anikin", "Name[de]": "Anton Anikin", - "Name[el]": "Anton Anikin", "Name[en_GB]": "Anton Anikin", "Name[es]": "Anton Anikin", "Name[et]": "Anton Anikin", "Name[fr]": "Anton Anikin", "Name[gl]": "Anton Anikin", "Name[it]": "Anton Anikin", "Name[nl]": "Anton Anikin", "Name[nn]": "Anton Anikin", "Name[pl]": "Anton Anikin", "Name[pt]": "Anton Anikin", "Name[pt_BR]": "Anton Anikin", "Name[ru]": "Антон Аникин", "Name[sk]": "Anton Anikin", "Name[sl]": "Anton Anikin", "Name[sv]": "Anton Anikin", "Name[tr]": "Anton Anikin", "Name[uk]": "Anton Anikin", "Name[x-test]": "xxAnton Anikinxx", "Name[zh_CN]": "Anton Anikin" } ], "Category": "Analyzers", "Description": "This plugin integrates Clazy to KDevelop", "Description[ca@valencia]": "Aquest connector integra el Clazy al KDevelop", "Description[ca]": "Aquest connector integra el Clazy al KDevelop", "Description[cs]": "Tento modul integruje podporu pro Clazy v KDevelop", "Description[de]": "Dieses Modul integriert Clazy in KDevelop", - "Description[el]": "Το πρόσθετο αυτό ενσωματώνει το Clazy στο KDevelop", "Description[en_GB]": "This plugin integrates Clazy to KDevelop", "Description[es]": "Este complemento integra Clazy en KDevelop", - "Description[et]": "See plugin lõimib Clazy KDevelopiga", "Description[fr]": "Ce module externe intègre Clazy dans KDevelop", "Description[gl]": "Este complemento integra Clazy en KDevelop", "Description[it]": "Questa estensione integra Clazy in KDevelop", "Description[nl]": "Deze plug-in integreert Clazy in KDevelop", "Description[pl]": "Wplata Clazy w KDevelop", "Description[pt]": "Este 'plugin' integra o Clazy no KDevelop", "Description[pt_BR]": "Este plugin integra o Clazy ao KDevelop", - "Description[sk]": "Tento plugin integruje Clazy do KDevelop", + "Description[sk]": "Tento plugin integruje Clazy do KDevelop.", "Description[sv]": "Insticksprogrammet integrerar Clazy i KDevelop", "Description[uk]": "За допомогою цього додатка можна інтегрувати Clazy до KDevelop", "Description[x-test]": "xxThis plugin integrates Clazy to KDevelopxx", "Description[zh_CN]": "此插件整合 Clazy 到 KDevelop", "Icon": "clazy", "Id": "kdevclazy", "License": "GPL", "Name": "Clazy Support", "Name[ca@valencia]": "Implementació de Clazy", "Name[ca]": "Implementació de Clazy", "Name[cs]": "Podpora Clazy", "Name[de]": "Clazy-Unterstützung", - "Name[el]": "Υποστήριξη Clazy", "Name[en_GB]": "Clazy Support", "Name[es]": "Implementación de Clazy", - "Name[et]": "Clazy toetus", "Name[fr]": "Prise en charge de Clazy", "Name[gl]": "Compatibilidade con Clazy", "Name[it]": "Supporto per Clazy", "Name[nl]": "Clazy-ondersteuning", "Name[pl]": "Obsługa Clazy", "Name[pt]": "Suporte para o Clazy", "Name[pt_BR]": "Suporte à Clazy", "Name[sk]": "Podpora Clazy", "Name[sv]": "Clazy-stöd", "Name[uk]": "Підтримка Clazy", "Name[x-test]": "xxClazy Supportxx", "Name[zh_CN]": "Clazy 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IBuildSystemManager" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/clazy/utils.cpp b/plugins/clazy/utils.cpp index 1785038ffb..ba2b3897d4 100644 --- a/plugins/clazy/utils.cpp +++ b/plugins/clazy/utils.cpp @@ -1,201 +1,201 @@ /* This file is part of KDevelop Copyright 2018 Anton Anikin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "utils.h" // KDevPlatform #include #include #include // KF #include // Qt #include #include #include namespace Clazy { QString prettyPathName(const QUrl& path) { return KDevelop::ICore::self()->projectController()->prettyFileName(path, KDevelop::IProjectController::FormatPlain); } // Very simple Markdown parser/converter. Does not provide full Markdown language support and // was tested only with Clazy documentation. class MarkdownConverter { public: MarkdownConverter() { tagStart.resize(STATE_COUNT); tagEnd.resize(STATE_COUNT); tagStart[EMPTY].clear(); tagEnd [EMPTY].clear(); tagStart[HEADING] = QStringLiteral(""); tagEnd [HEADING] = QStringLiteral(""); tagStart[PARAGRAPH] = QStringLiteral("

"); tagEnd [PARAGRAPH] = QStringLiteral("

"); tagStart[PREFORMATTED] = QStringLiteral("
");
         tagEnd  [PREFORMATTED] = QStringLiteral("
"); tagStart[LIST] = QStringLiteral("
  • "); tagEnd [LIST] = QStringLiteral("
"); } ~MarkdownConverter() = default; QString toHtml(const QString& markdown) { const QRegularExpression hRE(QStringLiteral("(#+) (.+)")); QRegularExpressionMatch match; state = EMPTY; html.clear(); html += QStringLiteral(""); - auto lines = markdown.split(QLatin1Char('\n')); + const auto lines = markdown.split(QLatin1Char('\n')); for (auto line : lines) { if (line.isEmpty()) { setState(EMPTY); continue; } if (line.startsWith(QLatin1Char('#'))) { auto match = hRE.match(line); if (match.hasMatch()) { setState(HEADING); html += match.captured(2); setState(EMPTY); if (match.capturedRef(1).size() == 1) { html += QStringLiteral("
"); } } continue; } if (line.startsWith(QLatin1String("```"))) { setState((state == PREFORMATTED) ? EMPTY : PREFORMATTED); continue; } if (line.startsWith(QLatin1String(" "))) { if (state == EMPTY) { setState(PREFORMATTED); } } else if ( line.startsWith(QLatin1String("- ")) || line.startsWith(QLatin1String("* "))) { // force close and reopen list - this fixes cases when we don't have // separator line between items setState(EMPTY); setState(LIST); line.remove(0, 2); } if (state == EMPTY) { setState(PARAGRAPH); } processLine(line); } setState(EMPTY); html += QStringLiteral(""); return html.join(QLatin1Char('\n')); } private: enum STATE { EMPTY, HEADING, PARAGRAPH, PREFORMATTED, LIST, STATE_COUNT }; void setState(int newState) { if (state == newState) { return; } if (state != EMPTY) { html += tagEnd[state]; } if (newState != EMPTY) { html += tagStart[newState]; } state = newState; } void processLine(QString& line) { static const QRegularExpression ttRE(QStringLiteral("`([^`]+)`")); static const QRegularExpression bdRE(QStringLiteral("\\*\\*([^\\*]+)\\*\\*")); static const QRegularExpression itRE(QStringLiteral("[^\\*]\\*([^\\*]+)\\*[^\\*]")); static auto applyRE = [](const QRegularExpression& re, QString& line, const QString& tag) { auto i = re.globalMatch(line); while (i.hasNext()) { auto match = i.next(); line.replace(match.captured(0), QStringLiteral("<%1>%2").arg(tag, match.captured(1))); } }; if (state != PREFORMATTED) { line.replace(QLatin1Char('&'), QLatin1String("&")); line.replace(QLatin1Char('<'), QLatin1String("<")); line.replace(QLatin1Char('>'), QLatin1String(">")); line.replace(QLatin1Char('\"'), QLatin1String(""")); line.replace(QLatin1Char('\''), QLatin1String("'")); applyRE(ttRE, line, QStringLiteral("tt")); applyRE(bdRE, line, QStringLiteral("b")); applyRE(itRE, line, QStringLiteral("i")); } html += line; } private: int state; QVector tagStart; QVector tagEnd; QStringList html; }; QString markdown2html(const QByteArray& markdown) { MarkdownConverter converter; return converter.toHtml(QString::fromUtf8(markdown)); } } diff --git a/plugins/cmake/cmakeutils.cpp b/plugins/cmake/cmakeutils.cpp index b3fba48749..c548a574d3 100644 --- a/plugins/cmake/cmakeutils.cpp +++ b/plugins/cmake/cmakeutils.cpp @@ -1,742 +1,743 @@ /* KDevelop CMake Support * * Copyright 2009 Andreas Pakulat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakeutils.h" #include "cmakeprojectdata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icmakedocumentation.h" #include "cmakebuilddirchooser.h" #include "settings/cmakecachemodel.h" #include "debug.h" #include "cmakebuilderconfig.h" #include #include "parser/cmakelistsparser.h" using namespace KDevelop; namespace Config { namespace Old { static const QString currentBuildDirKey = QStringLiteral("CurrentBuildDir"); static const QString oldcmakeExecutableKey = QStringLiteral("CMake Binary"); // Todo: Remove at some point static const QString currentBuildTypeKey = QStringLiteral("CurrentBuildType"); static const QString currentInstallDirKey = QStringLiteral("CurrentInstallDir"); static const QString currentEnvironmentKey = QStringLiteral("CurrentEnvironment"); static const QString currentExtraArgumentsKey = QStringLiteral("Extra Arguments"); static const QString currentCMakeExecutableKey = QStringLiteral("Current CMake Binary"); static const QString projectRootRelativeKey = QStringLiteral("ProjectRootRelative"); static const QString projectBuildDirs = QStringLiteral("BuildDirs"); } static const QString buildDirIndexKey_ = QStringLiteral("Current Build Directory Index"); static const QString buildDirOverrideIndexKey = QStringLiteral("Temporary Build Directory Index"); static const QString buildDirCountKey = QStringLiteral("Build Directory Count"); //the used builddir will change for every runtime static QString buildDirIndexKey() { const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); return buildDirIndexKey_ + QLatin1Char('-') + currentRuntime; } namespace Specific { static const QString buildDirPathKey = QStringLiteral("Build Directory Path"); // TODO: migrate to more generic & consistent key term "CMake Executable" // Support the old "CMake Binary" key too for backwards compatibility during // a reasonable transition period. Both keys are saved at least until 5.2.0 // is released. Import support for the old key will need to remain for a // considerably longer period, ideally. static const QString cmakeBinaryKey = QStringLiteral("CMake Binary"); static const QString cmakeExecutableKey = QStringLiteral("CMake Executable"); static const QString cmakeBuildTypeKey = QStringLiteral("Build Type"); static const QString cmakeInstallDirKey = QStringLiteral("Install Directory"); static const QString cmakeEnvironmentKey = QStringLiteral("Environment Profile"); static const QString cmakeArgumentsKey = QStringLiteral("Extra Arguments"); static const QString buildDirRuntime = QStringLiteral("Runtime"); } static const QString groupNameBuildDir = QStringLiteral("CMake Build Directory %1"); static const QString groupName = QStringLiteral("CMake"); } // namespace Config namespace { KConfigGroup baseGroup( KDevelop::IProject* project ) { if (!project) return KConfigGroup(); return project->projectConfiguration()->group( Config::groupName ); } KConfigGroup buildDirGroup( KDevelop::IProject* project, int buildDirIndex ) { return baseGroup(project).group( Config::groupNameBuildDir.arg(buildDirIndex) ); } bool buildDirGroupExists( KDevelop::IProject* project, int buildDirIndex ) { return baseGroup(project).hasGroup( Config::groupNameBuildDir.arg(buildDirIndex) ); } QString readBuildDirParameter( KDevelop::IProject* project, const QString& key, const QString& aDefault, int buildDirectory ) { const int buildDirIndex = buildDirectory<0 ? CMake::currentBuildDirIndex(project) : buildDirectory; if (buildDirIndex >= 0) return buildDirGroup( project, buildDirIndex ).readEntry( key, aDefault ); else return aDefault; } void writeBuildDirParameter( KDevelop::IProject* project, const QString& key, const QString& value ) { int buildDirIndex = CMake::currentBuildDirIndex(project); if (buildDirIndex >= 0) { KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex ); buildDirGrp.writeEntry( key, value ); } else { qCWarning(CMAKE) << "cannot write key" << key << "(" << value << ")" << "when no builddir is set!"; } } void writeProjectBaseParameter( KDevelop::IProject* project, const QString& key, const QString& value ) { KConfigGroup baseGrp = baseGroup(project); baseGrp.writeEntry( key, value ); } void setBuildDirRuntime( KDevelop::IProject* project, const QString& name) { writeBuildDirParameter(project, Config::Specific::buildDirRuntime, name); } QString buildDirRuntime( KDevelop::IProject* project, int builddir) { return readBuildDirParameter(project, Config::Specific::buildDirRuntime, QString(), builddir); } } // namespace namespace CMake { KDevelop::Path::List resolveSystemDirs(KDevelop::IProject* project, const QStringList& dirs) { const KDevelop::Path buildDir(CMake::currentBuildDir(project)); const KDevelop::Path installDir(CMake::currentInstallDir(project)); KDevelop::Path::List newList; newList.reserve(dirs.size()); for (const QString& s : dirs) { KDevelop::Path dir; if(s.startsWith(QLatin1String("#[bin_dir]"))) { dir = KDevelop::Path(buildDir, s); } else if(s.startsWith(QLatin1String("#[install_dir]"))) { dir = KDevelop::Path(installDir, s); } else { dir = KDevelop::Path(s); } // qCDebug(CMAKE) << "resolved" << s << "to" << d; if (!newList.contains(dir)) { newList.append(dir); } } return newList; } ///NOTE: when you change this, update @c defaultConfigure in cmakemanagertest.cpp bool checkForNeedingConfigure( KDevelop::IProject* project ) { auto currentRuntime = ICore::self()->runtimeController()->currentRuntime(); const QString currentRuntimeName = currentRuntime->name(); const KDevelop::Path builddir = currentBuildDir(project); const bool isValid = (buildDirRuntime(project, -1) == currentRuntimeName || buildDirRuntime(project, -1).isEmpty()) && builddir.isValid(); if( !isValid ) { auto addBuildDir = [project](const KDevelop::Path& buildFolder, const KDevelop::Path& installPrefix, const QString &extraArguments, const QString &buildType, const KDevelop::Path &cmakeExecutable){ int addedBuildDirIndex = buildDirCount( project ); // old count is the new index // Initialize the kconfig items with the values from the dialog, this ensures the settings // end up in the config file once the changes are saved qCDebug(CMAKE) << "adding to cmake config: new builddir index" << addedBuildDirIndex; qCDebug(CMAKE) << "adding to cmake config: builddir path " << buildFolder; qCDebug(CMAKE) << "adding to cmake config: installdir " << installPrefix; qCDebug(CMAKE) << "adding to cmake config: extra args" << extraArguments; qCDebug(CMAKE) << "adding to cmake config: build type " << buildType; qCDebug(CMAKE) << "adding to cmake config: cmake executable " << cmakeExecutable; qCDebug(CMAKE) << "adding to cmake config: environment "; CMake::setBuildDirCount( project, addedBuildDirIndex + 1 ); CMake::setCurrentBuildDirIndex( project, addedBuildDirIndex ); CMake::setCurrentBuildDir( project, buildFolder ); CMake::setCurrentInstallDir( project, installPrefix ); CMake::setCurrentExtraArguments( project, extraArguments ); CMake::setCurrentBuildType( project, buildType ); CMake::setCurrentCMakeExecutable(project, cmakeExecutable ); CMake::setCurrentEnvironment( project, QString() ); }; if (!currentRuntime->buildPath().isEmpty()) { const Path newBuilddir(currentRuntime->buildPath(), QLatin1String("build-") + currentRuntimeName + project->name()); const Path installPath(QString::fromUtf8(currentRuntime->getenv("KDEV_DEFAULT_INSTALL_PREFIX"))); addBuildDir(newBuilddir, installPath, {}, QStringLiteral("Debug"), {}); setBuildDirRuntime( project, currentRuntimeName ); return true; } CMakeBuildDirChooser bd; bd.setProject( project ); const auto builddirs = CMake::allBuildDirs(project); bd.setAlreadyUsed( builddirs ); bd.setShowAvailableBuildDirs(!builddirs.isEmpty()); bd.setCMakeExecutable(currentCMakeExecutable(project)); if( !bd.exec() ) { return false; } if (bd.reuseBuilddir()) { CMake::setCurrentBuildDirIndex( project, bd.alreadyUsedIndex() ); } else { addBuildDir(bd.buildFolder(), bd.installPrefix(), bd.extraArguments(), bd.buildType(), bd.cmakeExecutable()); } setBuildDirRuntime( project, currentRuntimeName ); return true; } else if( !QFile::exists( KDevelop::Path(builddir, QStringLiteral("CMakeCache.txt")).toLocalFile() ) || //TODO: maybe we could use the builder for that? !(QFile::exists( KDevelop::Path(builddir, QStringLiteral("Makefile")).toLocalFile() ) || QFile::exists( KDevelop::Path(builddir, QStringLiteral("build.ninja")).toLocalFile() ) ) ) { // User entered information already, but cmake hasn't actually been run yet. setBuildDirRuntime( project, currentRuntimeName ); return true; } setBuildDirRuntime( project, currentRuntimeName ); return false; } QHash enumerateTargets(const KDevelop::Path& targetsFilePath, const QString& sourceDir, const KDevelop::Path &buildDir) { const QString buildPath = buildDir.toLocalFile(); QHash targets; QFile targetsFile(targetsFilePath.toLocalFile()); if (!targetsFile.open(QIODevice::ReadOnly)) { qCDebug(CMAKE) << "Couldn't find the Targets file in" << targetsFile.fileName(); } QTextStream targetsFileStream(&targetsFile); const QRegularExpression rx(QStringLiteral("^(.*)/CMakeFiles/(.*).dir$")); while (!targetsFileStream.atEnd()) { const QString line = targetsFileStream.readLine(); auto match = rx.match(line); if (!match.isValid()) qCDebug(CMAKE) << "invalid match for" << line; const QString sourcePath = match.captured(1).replace(buildPath, sourceDir); targets[KDevelop::Path(sourcePath)].append(match.captured(2)); } return targets; } KDevelop::Path projectRoot(KDevelop::IProject* project) { if (!project) { return {}; } return project->path().cd(CMake::projectRootRelative(project)); } KDevelop::Path currentBuildDir( KDevelop::IProject* project, int builddir ) { return KDevelop::Path(readBuildDirParameter( project, Config::Specific::buildDirPathKey, QString(), builddir )); } KDevelop::Path commandsFile(KDevelop::IProject* project) { auto currentBuildDir = CMake::currentBuildDir(project); if (currentBuildDir.isEmpty()) { return {}; } return KDevelop::Path(currentBuildDir, QStringLiteral("compile_commands.json")); } KDevelop::Path targetDirectoriesFile(KDevelop::IProject* project) { auto currentBuildDir = CMake::currentBuildDir(project); if (currentBuildDir.isEmpty()) { return {}; } return KDevelop::Path(currentBuildDir, QStringLiteral("CMakeFiles/TargetDirectories.txt")); } QString currentBuildType( KDevelop::IProject* project, int builddir ) { return readBuildDirParameter( project, Config::Specific::cmakeBuildTypeKey, QStringLiteral("Release"), builddir ); } QString findExecutable() { auto cmake = QStandardPaths::findExecutable(QStringLiteral("cmake")); #ifdef Q_OS_WIN if (cmake.isEmpty()) cmake = QStandardPaths::findExecutable(QStringLiteral("cmake"), { QStringLiteral("C:\\Program Files (x86)\\CMake\\bin"), QStringLiteral("C:\\Program Files\\CMake\\bin"), QStringLiteral("C:\\Program Files (x86)\\CMake 2.8\\bin"), QStringLiteral("C:\\Program Files\\CMake 2.8\\bin")}); #endif return cmake; } KDevelop::Path currentCMakeExecutable(KDevelop::IProject* project, int builddir) { auto defaultCMakeExecutable = CMakeBuilderSettings::self()->cmakeExecutable().toLocalFile(); if (!QFileInfo::exists(ICore::self()->runtimeController()->currentRuntime()->pathInHost(KDevelop::Path(defaultCMakeExecutable)).toLocalFile())) defaultCMakeExecutable = CMake::findExecutable(); if (project) { // check for "CMake Executable" but for now also "CMake Binary", falling back to the default. auto projectCMakeExecutable = readBuildDirParameter( project, Config::Specific::cmakeExecutableKey, readBuildDirParameter( project, Config::Specific::cmakeBinaryKey, defaultCMakeExecutable, builddir), builddir ); if (projectCMakeExecutable != defaultCMakeExecutable) { QFileInfo info(projectCMakeExecutable); if (!info.isExecutable()) { projectCMakeExecutable = defaultCMakeExecutable; } } return KDevelop::Path(projectCMakeExecutable); } return KDevelop::Path(defaultCMakeExecutable); } KDevelop::Path currentInstallDir( KDevelop::IProject* project, int builddir ) { return KDevelop::Path(readBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, QString(), builddir )); } QString projectRootRelative( KDevelop::IProject* project ) { return baseGroup(project).readEntry( Config::Old::projectRootRelativeKey, "." ); } bool hasProjectRootRelative(KDevelop::IProject* project) { return baseGroup(project).hasKey( Config::Old::projectRootRelativeKey ); } QString currentExtraArguments( KDevelop::IProject* project, int builddir ) { return readBuildDirParameter( project, Config::Specific::cmakeArgumentsKey, QString(), builddir ); } void setCurrentInstallDir( KDevelop::IProject* project, const KDevelop::Path& path ) { writeBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, path.toLocalFile() ); } void setCurrentBuildType( KDevelop::IProject* project, const QString& type ) { writeBuildDirParameter( project, Config::Specific::cmakeBuildTypeKey, type ); } void setCurrentCMakeExecutable(KDevelop::IProject* project, const KDevelop::Path& path) { // maintain compatibility with older versions for now writeBuildDirParameter(project, Config::Specific::cmakeBinaryKey, path.toLocalFile()); writeBuildDirParameter(project, Config::Specific::cmakeExecutableKey, path.toLocalFile()); } void setCurrentBuildDir( KDevelop::IProject* project, const KDevelop::Path& path ) { writeBuildDirParameter( project, Config::Specific::buildDirPathKey, path.toLocalFile() ); } void setProjectRootRelative( KDevelop::IProject* project, const QString& relative) { writeProjectBaseParameter( project, Config::Old::projectRootRelativeKey, relative ); } void setCurrentExtraArguments( KDevelop::IProject* project, const QString& string) { writeBuildDirParameter( project, Config::Specific::cmakeArgumentsKey, string ); } QString currentEnvironment(KDevelop::IProject* project, int builddir) { return readBuildDirParameter( project, Config::Specific::cmakeEnvironmentKey, QString(), builddir ); } int currentBuildDirIndex( KDevelop::IProject* project ) { KConfigGroup baseGrp = baseGroup(project); if ( baseGrp.hasKey( Config::buildDirOverrideIndexKey ) ) return baseGrp.readEntry( Config::buildDirOverrideIndexKey, -1 ); else if (baseGrp.hasKey(Config::buildDirIndexKey())) return baseGrp.readEntry( Config::buildDirIndexKey(), -1 ); else return baseGrp.readEntry( Config::buildDirIndexKey_, -1 ); // backwards compatibility } void setCurrentBuildDirIndex( KDevelop::IProject* project, int buildDirIndex ) { writeProjectBaseParameter( project, Config::buildDirIndexKey(), QString::number (buildDirIndex) ); } void setCurrentEnvironment( KDevelop::IProject* project, const QString& environment ) { writeBuildDirParameter( project, Config::Specific::cmakeEnvironmentKey, environment ); } void initBuildDirConfig( KDevelop::IProject* project ) { int buildDirIndex = currentBuildDirIndex( project ); if (buildDirCount(project) <= buildDirIndex ) setBuildDirCount( project, buildDirIndex + 1 ); } int buildDirCount( KDevelop::IProject* project ) { return baseGroup(project).readEntry( Config::buildDirCountKey, 0 ); } void setBuildDirCount( KDevelop::IProject* project, int count ) { writeProjectBaseParameter( project, Config::buildDirCountKey, QString::number(count) ); } void removeBuildDirConfig( KDevelop::IProject* project ) { int buildDirIndex = currentBuildDirIndex( project ); if ( !buildDirGroupExists( project, buildDirIndex ) ) { qCWarning(CMAKE) << "build directory config" << buildDirIndex << "to be removed but does not exist"; return; } int bdCount = buildDirCount(project); setBuildDirCount( project, bdCount - 1 ); removeOverrideBuildDirIndex( project ); setCurrentBuildDirIndex( project, -1 ); // move (rename) the upper config groups to keep the numbering // if there's nothing to move, just delete the group physically if (buildDirIndex + 1 == bdCount) buildDirGroup( project, buildDirIndex ).deleteGroup(); else for (int i = buildDirIndex + 1; i < bdCount; ++i) { KConfigGroup src = buildDirGroup( project, i ); KConfigGroup dest = buildDirGroup( project, i - 1 ); dest.deleteGroup(); src.copyTo(&dest); src.deleteGroup(); } } QHash readCacheValues(const KDevelop::Path& cmakeCachePath, QSet variables) { QHash ret; QFile file(cmakeCachePath.toLocalFile()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(CMAKE) << "couldn't open CMakeCache.txt" << cmakeCachePath; return ret; } QTextStream in(&file); while (!in.atEnd() && !variables.isEmpty()) { QString line = in.readLine().trimmed(); if(!line.isEmpty() && line[0].isLetter()) { CacheLine c; c.readLine(line); if(!c.isCorrect()) continue; if (variables.remove(c.name())) { ret[c.name()] = c.value(); } } } return ret; } void updateConfig( KDevelop::IProject* project, int buildDirIndex) { if (buildDirIndex < 0) return; KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex ); const KDevelop::Path builddir(buildDirGrp.readEntry( Config::Specific::buildDirPathKey, QString() )); const KDevelop::Path cacheFilePath( builddir, QStringLiteral("CMakeCache.txt")); const QMap keys = { { QStringLiteral("CMAKE_COMMAND"), Config::Specific::cmakeExecutableKey }, { QStringLiteral("CMAKE_INSTALL_PREFIX"), Config::Specific::cmakeInstallDirKey }, { QStringLiteral("CMAKE_BUILD_TYPE"), Config::Specific::cmakeBuildTypeKey } }; - const QHash cacheValues = readCacheValues(cacheFilePath, keys.keys().toSet()); + const QSet variables = keys.keys().toSet(); + const QHash cacheValues = readCacheValues(cacheFilePath, variables); for(auto it = cacheValues.constBegin(), itEnd = cacheValues.constEnd(); it!=itEnd; ++it) { const QString key = keys.value(it.key()); Q_ASSERT(!key.isEmpty()); // Use cache only when the config value is not set. Without this check we will always // overwrite values provided by the user in config dialog. if (buildDirGrp.readEntry(key).isEmpty() && !it.value().isEmpty()) { buildDirGrp.writeEntry( key, it.value() ); } } } void attemptMigrate( KDevelop::IProject* project ) { if ( !baseGroup(project).hasKey( Config::Old::projectBuildDirs ) ) { qCDebug(CMAKE) << "CMake settings migration: already done, exiting"; return; } KConfigGroup baseGrp = baseGroup(project); KDevelop::Path buildDir( baseGrp.readEntry( Config::Old::currentBuildDirKey, QString() ) ); int buildDirIndex = -1; const QStringList existingBuildDirs = baseGrp.readEntry( Config::Old::projectBuildDirs, QStringList() ); { // also, find current build directory in this list (we need an index, not path) QString currentBuildDirCanonicalPath = QDir( buildDir.toLocalFile() ).canonicalPath(); for( int i = 0; i < existingBuildDirs.count(); ++i ) { const QString& nextBuildDir = existingBuildDirs.at(i); if( QDir(nextBuildDir).canonicalPath() == currentBuildDirCanonicalPath ) { buildDirIndex = i; } } } int buildDirsCount = existingBuildDirs.count(); qCDebug(CMAKE) << "CMake settings migration: existing build directories" << existingBuildDirs; qCDebug(CMAKE) << "CMake settings migration: build directory count" << buildDirsCount; qCDebug(CMAKE) << "CMake settings migration: current build directory" << buildDir << "(index" << buildDirIndex << ")"; baseGrp.writeEntry( Config::buildDirCountKey, buildDirsCount ); baseGrp.writeEntry( Config::buildDirIndexKey(), buildDirIndex ); for (int i = 0; i < buildDirsCount; ++i) { qCDebug(CMAKE) << "CMake settings migration: writing group" << i << ": path" << existingBuildDirs.at(i); KConfigGroup buildDirGrp = buildDirGroup( project, i ); buildDirGrp.writeEntry( Config::Specific::buildDirPathKey, existingBuildDirs.at(i) ); } baseGrp.deleteEntry( Config::Old::currentBuildDirKey ); baseGrp.deleteEntry( Config::Old::currentCMakeExecutableKey ); baseGrp.deleteEntry( Config::Old::currentBuildTypeKey ); baseGrp.deleteEntry( Config::Old::currentInstallDirKey ); baseGrp.deleteEntry( Config::Old::currentEnvironmentKey ); baseGrp.deleteEntry( Config::Old::currentExtraArgumentsKey ); baseGrp.deleteEntry( Config::Old::projectBuildDirs ); } void setOverrideBuildDirIndex( KDevelop::IProject* project, int overrideBuildDirIndex ) { writeProjectBaseParameter( project, Config::buildDirOverrideIndexKey, QString::number(overrideBuildDirIndex) ); } void removeOverrideBuildDirIndex( KDevelop::IProject* project, bool writeToMainIndex ) { KConfigGroup baseGrp = baseGroup(project); if( !baseGrp.hasKey(Config::buildDirOverrideIndexKey) ) return; if( writeToMainIndex ) baseGrp.writeEntry( Config::buildDirIndexKey(), baseGrp.readEntry(Config::buildDirOverrideIndexKey) ); baseGrp.deleteEntry(Config::buildDirOverrideIndexKey); } ICMakeDocumentation* cmakeDocumentation() { return KDevelop::ICore::self()->pluginController()->extensionForPlugin(QStringLiteral("org.kdevelop.ICMakeDocumentation")); } QStringList allBuildDirs(KDevelop::IProject* project) { QStringList result; int bdCount = buildDirCount(project); result.reserve(bdCount); for (int i = 0; i < bdCount; ++i) result += buildDirGroup( project, i ).readEntry( Config::Specific::buildDirPathKey ); return result; } QString executeProcess(const QString& execName, const QStringList& args) { Q_ASSERT(!execName.isEmpty()); qCDebug(CMAKE) << "Executing:" << execName << "::" << args; QProcess p; QTemporaryDir tmp(QStringLiteral("kdevcmakemanager")); p.setWorkingDirectory( tmp.path() ); p.start(execName, args, QIODevice::ReadOnly); if(!p.waitForFinished()) { qCDebug(CMAKE) << "failed to execute:" << execName << args << p.exitStatus() << p.readAllStandardError(); } QByteArray b = p.readAllStandardOutput(); QString t = QString::fromUtf8(b.trimmed()); return t; } QStringList supportedGenerators() { QStringList generatorNames; bool hasNinja = ICore::self() && ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IProjectBuilder"), QStringLiteral("KDevNinjaBuilder")); if (hasNinja) generatorNames << QStringLiteral("Ninja"); #ifdef Q_OS_WIN // Visual Studio solution is the standard generator under windows, but we don't want to use // the VS IDE, so we need nmake makefiles generatorNames << QStringLiteral("NMake Makefiles") << QStringLiteral("MinGW Makefiles"); #endif generatorNames << QStringLiteral("Unix Makefiles"); return generatorNames; } QString defaultGenerator() { const QStringList generatorNames = supportedGenerators(); QString defGen = generatorNames.value(CMakeBuilderSettings::self()->generator()); if (defGen.isEmpty()) { qCWarning(CMAKE) << "Couldn't find builder with index " << CMakeBuilderSettings::self()->generator() << ", defaulting to 0"; CMakeBuilderSettings::self()->setGenerator(0); defGen = generatorNames.at(0); } return defGen; } QVector importTestSuites(const Path &buildDir, const QString &cmakeTestFileName) { const auto cmakeTestFile = Path(buildDir, cmakeTestFileName).toLocalFile() ; const auto contents = CMakeListsParser::readCMakeFile(cmakeTestFile); QVector tests; for (const auto& entry: contents) { if (entry.name == QLatin1String("add_test")) { auto args = entry.arguments; Test test; test.name = args.takeFirst().value; test.executable = args.takeFirst().value; test.arguments = kTransform(args, [](const CMakeFunctionArgument& arg) { return arg.value; }); tests += test; } else if (entry.name == QLatin1String("subdirs")) { tests += importTestSuites(Path(buildDir, entry.arguments.first().value)); } else if (entry.name == QLatin1String("include")) { // Include directive points directly to a .cmake file hosting the tests tests += importTestSuites(Path(buildDir, entry.arguments.first().value), QString()); } else if (entry.name == QLatin1String("set_tests_properties")) { if(entry.arguments.count() < 4 || entry.arguments.count() % 2) { qCWarning(CMAKE) << "found set_tests_properties() with unexpected number of arguments:" << entry.arguments.count(); continue; } if (tests.isEmpty() || entry.arguments.first().value != tests.last().name) { qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.first().value << " ...), but expected test " << tests.last().name; continue; } if (entry.arguments[1].value != QLatin1String("PROPERTIES")) { qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.first().value << entry.arguments.at(1).value << "...), but expected PROPERTIES as second argument"; continue; } Test &test = tests.last(); for (int i = 2; i < entry.arguments.count(); i += 2) test.properties[entry.arguments[i].value] = entry.arguments[i + 1].value; } } return tests; } QVector importTestSuites(const Path &buildDir) { return importTestSuites(buildDir, QStringLiteral("CTestTestfile.cmake")); } } diff --git a/plugins/cmake/duchain/usebuilder.cpp b/plugins/cmake/duchain/usebuilder.cpp index 3cd72a5fd6..fefb3fe562 100644 --- a/plugins/cmake/duchain/usebuilder.cpp +++ b/plugins/cmake/duchain/usebuilder.cpp @@ -1,65 +1,73 @@ /* KDevelop CMake Support * * Copyright 2014 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "usebuilder.h" #include #include #include -static QStringList initCommands() +static QSet initCommands() { QStringList ids = CMake::executeProcess(QStringLiteral("cmake"), QStringList(QStringLiteral("--help-command-list"))).split(QLatin1Char('\n')); + if (ids.isEmpty()) { + return {}; + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const auto secondIt = ids.constBegin() + 1; + return QSet(secondIt, ids.constEnd()); +#else ids.removeFirst(); - return ids; + return ids.toSet(); +#endif } // TODO: maybe share this again with codecompletionmodel and documentation -Q_GLOBAL_STATIC_WITH_ARGS(QSet, s_commands, (initCommands().toSet())) +Q_GLOBAL_STATIC_WITH_ARGS(QSet, s_commands, (initCommands())) UseBuilder::UseBuilder(const KDevelop::ReferencedTopDUContext& ctx) : m_ctx(ctx) { } void UseBuilder::startVisiting(CMakeContentIterator* node) { for(; node->hasNext(); ) { const CMakeFunctionDesc& func = node->next(); QString fname = func.name.toLower(); if (!s_commands->contains(fname)) { KDevelop::DUChainWriteLocker lock; KDevelop::Identifier nameid(fname); QList declarations = m_ctx->findDeclarations(nameid, func.range().start); if (!declarations.isEmpty()) { newUse( func.nameRange(), KDevelop::DeclarationPointer(declarations.first()) ); } } } } void UseBuilder::newUse(const KDevelop::RangeInRevision& sr, const KDevelop::DeclarationPointer& d) { m_ctx->createUse(m_ctx->indexForUsedDeclaration(d.data()), sr, 0); } diff --git a/plugins/cmake/kdevcmakedocumentation.json b/plugins/cmake/kdevcmakedocumentation.json index b822df6db9..28c2ef4723 100644 --- a/plugins/cmake/kdevcmakedocumentation.json +++ b/plugins/cmake/kdevcmakedocumentation.json @@ -1,101 +1,96 @@ { "KPlugin": { "Authors": [ { "Email": "aleixpol@kde.org", "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Documentation", "Description": "Allows KDevelop to provide CMake documentation", - "Description[bs]": "Dopišta KDevelopu da obezbjedi CMake dokumentaciju", "Description[ca@valencia]": "Permet al KDevelop proporcionar documentació de CMake", "Description[ca]": "Permet al KDevelop proporcionar documentació de CMake", "Description[cs]": "Umožňuje KDevelop poskytovat dokumentaci CMake", "Description[de]": "Ermöglicht das Bereitstellen von Dokumentation für CMake", - "Description[el]": "Επιτρέπει στο KDevelop να παρέχει τεκμηρίωση για το cmake", "Description[en_GB]": "Allows KDevelop to provide CMake documentation", "Description[es]": "Permite que KDevelop proporcione documentación de CMake", "Description[et]": "Võimaldab KDevelopil pakkuda CMake'i dokumentatsiooni", "Description[fi]": "Sallii KDevelop-ohjelman tarjota CMake-dokumentaation", "Description[fr]": "Permet à KDevelop de fournir la documentation CMake", "Description[gl]": "Permítelle a KDevelop fornecer documentación de CMake.", "Description[it]": "Permette a KDevelop di fornire la documentazione a CMake", "Description[nl]": "Staat KDevelop toe om CMake-documentatie te leveren", "Description[pl]": "Daje dostęp do dokumentacji CMake", "Description[pt]": "Permite ao KDevelop fornecer documentação sobre o CMake", "Description[pt_BR]": "Permite ao KDevelop fornecer documentação do CMake", "Description[sk]": "Povolí KDevelopu poskytnúť dokumentáciu CMake", "Description[sl]": "Omogoča, da KDevelop prikaže dokumentacijo za CMake", "Description[sv]": "Låter KDevelop tillhandahålla CMake-dokumentation", "Description[tr]": "KDevelop uygulamasına CMake belgelendirmesi sağlar", "Description[uk]": "Надає змогу KDevelop показувати документацію CMake", "Description[x-test]": "xxAllows KDevelop to provide CMake documentationxx", "Description[zh_CN]": "允许 KDevelop 提供 CMake 文档", "Icon": "cmake", "Id": "KDevCMakeDocumentation", "License": "GPL", "Name": "CMake Documentation", - "Name[bs]": "CMake Dokumentacija", "Name[ca@valencia]": "Documentació del CMake", "Name[ca]": "Documentació del CMake", "Name[cs]": "Dokumentace CMake", "Name[de]": "CMake-Dokumentation", - "Name[el]": "Τεκμηρίωση cmake", "Name[en_GB]": "CMake Documentation", "Name[es]": "Documentación de CMake", "Name[et]": "CMake'i dokumentatsioon", "Name[fi]": "CMake-dokumentaatio", "Name[fr]": "Documentation CMake", "Name[gl]": "Documentación de CMake", "Name[it]": "Documentazione CMake", "Name[nl]": "CMake-documentatie", "Name[pl]": "Dokumentacja CMake", "Name[pt]": "Documentação do CMake", "Name[pt_BR]": "Documentação do CMake", "Name[sk]": "Dokumentácia CMake", "Name[sl]": "Dokumentacija za CMake", "Name[sv]": "CMake-dokumentation", "Name[tr]": "CMake Belgelendirmesi", "Name[uk]": "Документація з CMake", "Name[x-test]": "xxCMake Documentationxx", "Name[zh_CN]": "CMake 文档", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IDocumentationProvider", "org.kdevelop.ICMakeDocumentation" ], "X-KDevelop-Mode": "GUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-cmake" ] } diff --git a/plugins/cmake/kdevcmakemanager.json b/plugins/cmake/kdevcmakemanager.json index cefb753703..4f2661df0f 100644 --- a/plugins/cmake/kdevcmakemanager.json +++ b/plugins/cmake/kdevcmakemanager.json @@ -1,113 +1,108 @@ { "KPlugin": { "Authors": [ { "Email": "mattr@kde.org", "Name": "Matt Rogers", "Name[ca@valencia]": "Matt Rogers", "Name[ca]": "Matt Rogers", "Name[cs]": "Matt Rogers", "Name[de]": "Matt Rogers", - "Name[el]": "Matt Rogers", "Name[en_GB]": "Matt Rogers", "Name[es]": "Matt Rogers", "Name[et]": "Matt Rogers", "Name[fi]": "Matt Rogers", "Name[fr]": "Matt Rogers", "Name[gl]": "Matt Rogers", "Name[it]": "Matt Rogers", "Name[nl]": "Matt Rogers", "Name[nn]": "Matt Rogers", "Name[pl]": "Matt Rogers", "Name[pt]": "Matt Rogers", "Name[pt_BR]": "Matt Rogers", "Name[ru]": "Matt Rogers", "Name[sk]": "Matt Rogers", "Name[sl]": "Matt Rogers", "Name[sv]": "Matt Rogers", "Name[tr]": "Matt Rogers", "Name[uk]": "Matt Rogers", "Name[x-test]": "xxMatt Rogersxx", "Name[zh_CN]": "Matt Rogers" } ], "Category": "Project Management", "Description": "Allows KDevelop to manage CMake-based projects", - "Description[bs]": "Dopušta da KDevelop upravlja CMake-baziranim projektima", "Description[ca@valencia]": "Permet que el KDevelop gestione projectes basats en CMake", "Description[ca]": "Permet que el KDevelop gestioni projectes basats en CMake", "Description[cs]": "Umožňuje KDevelop spravovat projekty založené na CMake", "Description[de]": "Erlaubt das Verwalten CMake-basierter Projekte mit KDevelop", - "Description[el]": "Επιτρέπει στο KDevelop τη διαχείριση έργων με βάση το cmake", "Description[en_GB]": "Allows KDevelop to manage CMake-based projects", "Description[es]": "Permite que KDevelop gestione proyectos basados en CMake", - "Description[et]": "Võimaldab KDevelopil hallata CMake'i-põhiseid projekte", + "Description[et]": "Võimaldab KDevelopil hallata CMake'i põhiseid projekte", "Description[fi]": "Sallii KDevelop-ohjelman hallinnoida CMake-perustaisia projekteja", "Description[fr]": "Permet à KDevelop de gérer des projets fondés sur CMake", "Description[gl]": "Permítelle a KDevelop xestionar proxectos baseados en CMake.", "Description[it]": "Permette a KDevelop di gestire i progetti basati su CMake", "Description[nl]": "Staat KDevelop toe om op CMake gebaseerde projecten te beheren", "Description[pl]": "Umożliwia zarządzanie projektami CMake", "Description[pt]": "Permitir ao KDevelop gerir projectos baseados no CMake", "Description[pt_BR]": "Permite ao KDevelop gerenciar projetos baseados em CMake", "Description[sk]": "Povolí KDevelopu spravovať projekty založené na CMake", "Description[sl]": "Omogoča, da KDevelop upravlja s projekti temelječimi na CMake", "Description[sv]": "Låter KDevelop hantera CMake-baserade projekt", "Description[tr]": "KDevelop uygulamasının CMake temelli projeleri yönetmesine izin verir", "Description[uk]": "Надає змогу KDevelop керувати заснованими на CMake проєктами", "Description[x-test]": "xxAllows KDevelop to manage CMake-based projectsxx", "Description[zh_CN]": "允许 KDevelop 管理基于 CMake 的工程", "Icon": "cmake", "Id": "KDevCMakeManager", "License": "GPL", "Name": "CMake Project Manager", - "Name[bs]": "Cmake menadžer projekta", "Name[ca@valencia]": "Gestor de projectes CMake", "Name[ca]": "Gestor de projectes CMake", "Name[cs]": "Správce projektů CMake", "Name[de]": "CMake-Projektverwaltung", - "Name[el]": "Διαχειριστής έργου cmake", "Name[en_GB]": "CMake Project Manager", "Name[es]": "Gestor de proyectos CMake", "Name[et]": "CMake'i projektihaldur", "Name[fi]": "CMake-projektinhallinta", "Name[fr]": "Gestionnaire de projet CMake", "Name[gl]": "Xestor de proxectos CMake", "Name[it]": "Gestore progetto CMake", "Name[nl]": "CMake-projectbeheerder", "Name[pl]": "Zarządzanie projektami CMake", - "Name[pt]": "Gestor de Projectos do CMake", + "Name[pt]": "Gestor de Projectos CMake", "Name[pt_BR]": "Gerenciador de Projeto CMake", "Name[sk]": "Projektový manažér CMake", "Name[sl]": "Upravljalnik projektov CMake", - "Name[sv]": "CMake projekthanterare", + "Name[sv]": "CMake-projekthantering", "Name[tr]": "CMake Proje Yöneticisi", "Name[uk]": "Керування проєктами CMake", "Name[x-test]": "xxCMake Project Managerxx", "Name[zh_CN]": "CMake 工程管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-IRequired": [ "org.kdevelop.IProjectBuilder@KDevCMakeBuilder" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBuildSystemManager", "org.kdevelop.IProjectFileManager", "ILanguageSupport" ], "X-KDevelop-Languages": [ "CMake" ], "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "GUI", "X-KDevelop-ProjectFilesFilter": [ "CMakeLists.txt" ], "X-KDevelop-ProjectFilesFilterDescription": "CMake Project Files", "X-KDevelop-SupportedMimeTypes": [ "text/x-cmake" ] } diff --git a/plugins/cmakebuilder/kdevcmakebuilder.json b/plugins/cmakebuilder/kdevcmakebuilder.json index 5cf5ad0677..8c73f8c4a1 100644 --- a/plugins/cmakebuilder/kdevcmakebuilder.json +++ b/plugins/cmakebuilder/kdevcmakebuilder.json @@ -1,70 +1,66 @@ { "KPlugin": { "Category": "Project Management", "Description": "Builds CMake Projects", - "Description[bs]": "Gradi CMake projekte", "Description[ca@valencia]": "Construeix projectes CMake", "Description[ca]": "Construeix projectes CMake", "Description[cs]": "Překládá projekty CMake", "Description[de]": "Erstellt CMake-Projekte", - "Description[el]": "Κατασκευάζει έργα cmake", "Description[en_GB]": "Builds CMake Projects", "Description[es]": "Compila proyectos de CMake", "Description[et]": "CMake'i projektide ehitamine", "Description[fi]": "Kääntää CMake-projekteja", "Description[fr]": "Construit des projets CMake", "Description[gl]": "Constrúe proxectos CMake.", "Description[it]": "Compila i progetti CMake", "Description[nl]": "Bouwt CMake-projecten", "Description[pl]": "Buduje projekty CMake", "Description[pt]": "Construtor de Projectos CMake no KDevelop", "Description[pt_BR]": "Compila projetos CMake", "Description[sk]": "Prekladá projekty CMake", "Description[sl]": "Izgradi projekte CMake", "Description[sv]": "Bygger CMake-projekt", "Description[tr]": "CMake Projelerini Derler", "Description[uk]": "Збирає проєкти CMake", "Description[x-test]": "xxBuilds CMake Projectsxx", "Description[zh_CN]": "构建 CMake 工程", "Icon": "cmake", "Id": "KDevCMakeBuilder", "Name": "CMake Project Builder", - "Name[bs]": "CMake graditelj projekta", "Name[ca@valencia]": "Constructor de projecte CMake", "Name[ca]": "Constructor de projecte CMake", "Name[cs]": "Překladač projektů CMake", "Name[de]": "Ersteller für CMake-Projekte", - "Name[el]": "Κατασκευαστής έργου cmake", "Name[en_GB]": "CMake Project Builder", "Name[es]": "Compilador de proyectos CMake", "Name[et]": "CMake'i projektiehitaja", "Name[fi]": "CMake-projektikäännin", "Name[fr]": "Constructeur de projet CMake", "Name[gl]": "Construtor de proxectos CMake", "Name[it]": "Compilatore progetto CMake", "Name[nl]": "CMake-projectbouwer", "Name[pl]": "Budowniczy projektu CMake", - "Name[pt]": "Compilador de Projectos do CMake", + "Name[pt]": "Compilador de Projectos CMake", "Name[pt_BR]": "Compilador de projeto do CMake", "Name[sk]": "Prekladač projektov CMake", "Name[sl]": "Izgrajevalnik projektov CMake", "Name[sv]": "CMake-projektbyggverktyg", "Name[tr]": "CMake Proje Oluşturucu", "Name[uk]": "Інструмент збирання проєктів CMake", "Name[x-test]": "xxCMake Project Builderxx", "Name[zh_CN]": "CMake 工程构建器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView", "org.kdevelop.IMakeBuilder" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectBuilder" ], "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectBuilder": "CMake" } diff --git a/plugins/codeutils/kdevcodeutils.json b/plugins/codeutils/kdevcodeutils.json index eb0343ffff..1f1d61ae07 100644 --- a/plugins/codeutils/kdevcodeutils.json +++ b/plugins/codeutils/kdevcodeutils.json @@ -1,69 +1,57 @@ { "KPlugin": { "Category": "Utilities", "Description": "Collection of various utilities that increase productivity while programming.", - "Description[ar]": "مجموعة من أدوات مختلفة تزيد الإنتاجيّة أثناء البرمجة.", "Description[ca@valencia]": "Col·lecció de diverses utilitats que augmenten la productivitat durant la programació.", "Description[ca]": "Col·lecció de diverses utilitats que augmenten la productivitat durant la programació.", "Description[cs]": "Kolekce různých pomůcek, pro zvýšení produktivity při programování.", "Description[de]": "Sammlung verschiedener Werkzeuge, die die Produktivität während des Programmierens erhöhen.", - "Description[el]": "Συλλογή διάφορων βοηθητικών προγραμμάτων που αυξάνουν την παραγωγικότητα κατά τον προγραμματισμό", "Description[en_GB]": "Collection of various utilities that increase productivity while programming.", "Description[es]": "Colección de diversas utilidades que incrementan la productividad al programar.", "Description[et]": "Mitmesugused tööriistad, mis suurendavad produktiivsust programmeerimisel.", "Description[fr]": "Collection d'utilitaires variés qui améliorent la productivité pour programmer.", "Description[gl]": "Colección de varias utilidades que aumentan a produtividade durante a programación.", "Description[it]": "Raccolta di vari programmi di utilità che aumentano la produttività durante la programmazione.", "Description[nl]": "Verzameling van hulpprogramma's die de productiviteit vergroot bij het programmeren.", "Description[pl]": "Zbiór różnych narzędzi zwiększających produktywność podczas programowania.", "Description[pt]": "Uma colecção de vários utilitários que aumentam a produtividade durante o desenvolvimento.", "Description[pt_BR]": "Uma coleção de vários utilitários que aumentam a produtividade durante a programação.", "Description[sk]": "Zbierka rôznych utilít, ktoré zvyšujú produktivitu počas programovania.", "Description[sl]": "Zbirka raznih pripomočkov, ki povečajo učinkovitost med programiranjem.", "Description[sv]": "Samling av diverse verktyg för att öka produktiviteten vid programmering.", "Description[tr]": "Programlama sırasında verimliliği artıran çeşitli yardımcı araçlar koleksiyonu.", "Description[uk]": "Збірка різноманітних допоміжних програм, яка підвищує продуктивність програмування.", "Description[x-test]": "xxCollection of various utilities that increase productivity while programming.xx", "Description[zh_CN]": "多种提升编程生产力的工具集合。", - "Description[zh_TW]": "各種可以增加寫程式效率的工具的收藏。", "Icon": "help-hint", "Id": "kdevcodeutils", "Name": "Code Utilities", - "Name[ar]": "أدوات الشِّفرات", - "Name[bs]": "Kodni alati", "Name[ca@valencia]": "Utilitats de codi", "Name[ca]": "Utilitats de codi", - "Name[da]": "Kodeværktøjer", "Name[de]": "Quelltext-Werkzeuge", - "Name[el]": "Βοηθητικά προγράμματα", "Name[en_GB]": "Code Utilities", "Name[es]": "Utilidades de código fuente", "Name[et]": "Kooditööriistad", "Name[fr]": "Utilitaires de code", "Name[gl]": "Utilidades de código", - "Name[hu]": "Kód segédprogramok", "Name[it]": "Utilità per il codice", - "Name[kk]": "Код утилиталары", "Name[nb]": "Kodeverktøy", - "Name[nds]": "Kode-Warktüüch", "Name[nl]": "Hulpmiddelen bij coderen", "Name[pl]": "Narzędzia kodu", "Name[pt]": "Utilitários de Código", "Name[pt_BR]": "Utilitários de código", "Name[ru]": "Утилиты для работы с кодом", "Name[sk]": "Nástroje kódu", "Name[sl]": "Pripomočki za kodo", "Name[sv]": "Kodverktyg", "Name[tr]": "Kod Araçları", - "Name[ug]": "كود قوراللىرى", "Name[uk]": "Допоміжні засоби", "Name[x-test]": "xxCode Utilitiesxx", "Name[zh_CN]": "代码工具", - "Name[zh_TW]": "寫程式實用工具", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/contextbrowser/kdevcontextbrowser.json b/plugins/contextbrowser/kdevcontextbrowser.json index 7906362dc2..98ee8c49bb 100644 --- a/plugins/contextbrowser/kdevcontextbrowser.json +++ b/plugins/contextbrowser/kdevcontextbrowser.json @@ -1,108 +1,94 @@ { "KPlugin": { "Authors": [ { "Name": "David Nolden", "Name[ca@valencia]": "David Nolden", "Name[ca]": "David Nolden", "Name[cs]": "David Nolden", "Name[de]": "David Nolden", - "Name[el]": "David Nolden", "Name[en_GB]": "David Nolden", "Name[es]": "David Nolden", "Name[et]": "David Nolden", "Name[fi]": "David Nolden", "Name[fr]": "David Nolden", "Name[gl]": "David Nolden", "Name[it]": "David Nolden", "Name[nl]": "David Nolden", "Name[nn]": "David Nolden", "Name[pl]": "David Nolden", "Name[pt]": "David Nolden", "Name[pt_BR]": "David Nolden", "Name[ru]": "David Nolden", "Name[sk]": "David Nolden", "Name[sl]": "David Nolden", "Name[sv]": "David Nolden", "Name[tr]": "David Nolden", "Name[uk]": "David Nolden", "Name[x-test]": "xxDavid Noldenxx", "Name[zh_CN]": "David Nolden" } ], "Category": "Core", "Description": "This plugin shows information about the current language context in a side view, and highlights relevant declarations and uses.", "Description[ca@valencia]": "Aquest connector mostra informació quant al context del llenguatge actual en una vista lateral i ressalta les declaracions i els usos apropiats.", "Description[ca]": "Aquest connector mostra informació quant al context del llenguatge actual en una vista lateral i ressalta les declaracions i els usos apropiats.", "Description[cs]": "Tento zásuvný modul ukazuje v bočním pohledu informace o současném jazykovém kontextu a zvýrazňuje důležité deklarace a použití.", "Description[de]": "Dieses Modul zeigt Informationen über den aktuellen Sprachkontext an und hebt die wichtigen Deklarationen und Vorkommen hervor.", - "Description[el]": "Αυτό το πρόσθετο εμφανίζει πληροφορίες σχετικά με την τρέχουσα γλώσσα σε μια πλευρική προβολή, και τονίζει τις σχετικές δηλώσεις και χρήσεις.", "Description[en_GB]": "This plugin shows information about the current language context in a side view, and highlights relevant declarations and uses.", "Description[es]": "Este complemento muestra información de contexto sobre el lenguaje actual en una vista lateral, resaltando declaraciones relevantes y sus usos.", "Description[et]": "See plugin pakub külgvaates teavet aktiivse keele konteksti kohta ning tõstab esile asjakohased deklaratsioonid ja kasutused.", "Description[fr]": "Ce module affiche des informations à propos du contexte du langage courant dans une vue latérale, et met en évidence les déclarations et utilisations reliées.", "Description[gl]": "Este complemento mostra información sobre o contexto da linguaxe actual nunha vista lateral, e realza as declaracións e utilizacións relevantes.", "Description[it]": "Questa estensione mostra le informazioni sul contesto del linguaggio corrente in una vista laterale, mettendo in evidenza le dichiarazioni e gli usi.", "Description[nl]": "Deze plugin toont informatie over de huidige taalcontext in een zijvak en accentueert relevante declaraties en gebruik.", "Description[pl]": "Pokazuje dane dotyczące kontekstu bieżącego języka w widoku bocznym, podświetla deklaracje i ich użycia.", "Description[pt]": "Este 'plugin' mostra informações sobre o contexto da linguagem actual numa vista lateral, assim como realça as declarações e utilizações relevantes.", "Description[pt_BR]": "Este plugin mostra informações sobre a linguagem utilizada no momento, numa visão lateral, e destaca declarações relevantes e seus usos.", "Description[sk]": "Tento plugin zobrazuje informácie o aktuálnom jazykovom kontexte v božnom pohľade, a zvýrazní relevantné deklarácie a použitia.", "Description[sl]": "Vstavek prikazuje podatke o trenutnem kontekstu jezika in poudari pomembne deklaracije in uporabe.", "Description[sv]": "Insticksprogrammet visar information om nuvarande språksammanhang i en sidovy, och markerar relevanta deklarationer och användningar.", "Description[tr]": "Bu eklenti yan görünümde mevcut dil bağlamıyla ilgili bilgi gösterir, ve ilgili tanımlamaları ve kullanımları vurgular.", "Description[uk]": "За допомогою цього додатка можна переглянути у перегляді на бічній панелі відомості про поточний контекст мови, а також підсвітити пов’язані оголошення і випадки використання.", "Description[x-test]": "xxThis plugin shows information about the current language context in a side view, and highlights relevant declarations and uses.xx", "Description[zh_CN]": "此插件在侧边视图中显示关于当前语言上下文的信息,并加亮突出相关的声明和调用。", - "Description[zh_TW]": "此外掛程式顯示關於目前語言的資訊,並將相關的宣告與使用突顯出來。", "Icon": "code-context", "Id": "kdevcontextbrowser", "License": "GPL", "Name": "Code Browser", - "Name[ar]": "متصّفح الشِّفرة", - "Name[bg]": "Браузър за низове", - "Name[bs]": "Pregledač koda", "Name[ca@valencia]": "Navegador de codi", "Name[ca]": "Navegador de codi", "Name[cs]": "Prohlížeč kódu", - "Name[da]": "Kodebrowser", "Name[de]": "Quelltext-Browser", - "Name[el]": "Περιηγητής κώδικα", "Name[en_GB]": "Code Browser", "Name[es]": "Navegador de código", "Name[et]": "Koodibrauser", "Name[fr]": "Navigateur de code", "Name[gl]": "Navegador do código", - "Name[hu]": "Kódböngésző", "Name[it]": "Navigatore del codice", - "Name[kk]": "Код шолғышы", "Name[nb]": "Kodeleser", - "Name[nds]": "Kodekieker", "Name[nl]": "Broncode-browser", - "Name[pa]": "ਕੋਡ ਬਰਾਊਜ਼ਰ", "Name[pl]": "Przeglądarka kodu", "Name[pt]": "Navegador do Código", "Name[pt_BR]": "Navegador de código", "Name[ru]": "Навигация по коду", "Name[sk]": "Prehliadač kódu", "Name[sl]": "Brskalnik po kodi", "Name[sv]": "Kodbläddrare", "Name[tr]": "Kod Tarayıcı", - "Name[ug]": "كود كۆرگۈ", "Name[uk]": "Переглядач коду", "Name[x-test]": "xxCode Browserxx", "Name[zh_CN]": "代码浏览器", - "Name[zh_TW]": "源碼瀏覽器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IQuickOpen" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IContextBrowser" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/cppcheck/kdevcppcheck.json b/plugins/cppcheck/kdevcppcheck.json index 03539ba1f1..e90ea75d16 100644 --- a/plugins/cppcheck/kdevcppcheck.json +++ b/plugins/cppcheck/kdevcppcheck.json @@ -1,90 +1,85 @@ { "KPlugin": { "Authors": [ { "Name": "Christoph Thielecke", "Name[ca@valencia]": "Christoph Thielecke", "Name[ca]": "Christoph Thielecke", "Name[cs]": "Christoph Thielecke", "Name[de]": "Christoph Thielecke", - "Name[el]": "Christoph Thielecke", "Name[en_GB]": "Christoph Thielecke", "Name[es]": "Christoph Thielecke", "Name[et]": "Christoph Thielecke", "Name[fi]": "Christoph Thielecke", "Name[fr]": "Christoph Thielecke", "Name[gl]": "Christoph Thielecke", "Name[it]": "Christoph Thielecke", "Name[nl]": "Christoph Thielecke", "Name[nn]": "Christoph Thielecke", "Name[pl]": "Christoph Thielecke", "Name[pt]": "Christoph Thielecke", "Name[pt_BR]": "Christoph Thielecke", "Name[ru]": "Christoph Thielecke", "Name[sk]": "Christoph Thielecke", "Name[sl]": "Christoph Thielecke", "Name[sv]": "Christoph Thielecke", "Name[tr]": "Christoph Thielecke", "Name[uk]": "Christoph Thielecke", "Name[x-test]": "xxChristoph Thieleckexx", "Name[zh_CN]": "Christoph Thielecke" } ], "Category": "Analyzers", "Description": "This plugin integrates Cppcheck (static analysis tool) to KDevelop", "Description[ca@valencia]": "Aquest connector integra el Cppcheck (eina per a l'anàlisi estàtica) al KDevelop", "Description[ca]": "Aquest connector integra el Cppcheck (eina per a l'anàlisi estàtica) al KDevelop", "Description[cs]": "Tento modul integruje Cppcheck (statický analytický nástroj) do KDevelop", "Description[de]": "Dieses Modul integriert Cppcheck (Werkzeug zur statischen Analyse) in KDevelop", - "Description[el]": "Αυτό το πρόσθετο ενσωματώνει το cppcheck (εργαλείο στατικής ανάλυσης) στο KDevelop", "Description[en_GB]": "This plugin integrates Cppcheck (static analysis tool) to KDevelop", "Description[es]": "Este complemento integra Cppcheck (herramienta de análisis sintáctico) en KDevelop", - "Description[et]": "See plugin lõimib Cppchecki (staatilise analüüsi tööriist) KDevelopiga", "Description[fr]": "Ce module externe intègre Cppcheck (outil d'analyse statique) à KDevelop", "Description[gl]": "Este complemento integra Cppcheck (unha ferramenta de análise estática) con KDevelop.", "Description[it]": "Questa estensione integra Cppcheck (strumento di analisi statica) in KDevelop", "Description[nl]": "Deze plugin integreert Cppcheck (statisch analyse hulpmiddel) in KDevelop", "Description[pl]": "Wplata Cppcheck (statyczne narzędzie analizy) w KDevelop", "Description[pt]": "Este 'plugin' integra o Cppcheck (ferramenta de análise estática) no KDevelop", "Description[pt_BR]": "Este plugin integra o Cppcheck (ferramenta de análise estática) no KDevelop", "Description[sk]": "Tento plugin integruje Cppcheck (nástroj na statickú analýzy) pre KDevelop", "Description[sl]": "Ta vstavek v KDevelop vgradi Cppcheck (orodje za statično preučevanje)", "Description[sv]": "Insticksprogrammet integrerar Cppcheck (statiskt analysverktyg) i KDevelop", "Description[tr]": "Bu eklenti Cppcheck uygulamasını (istatistik analiz aracı) KDevelop ile bütünleştirir", "Description[uk]": "За допомогою цього додатка можна інтегрувати Cppcheck (засіб статичного аналізу) до KDevelop", "Description[x-test]": "xxThis plugin integrates Cppcheck (static analysis tool) to KDevelopxx", "Description[zh_CN]": "此插件集成了 Cppcheck (静态分析工具) 到 KDevelop", "Icon": "cppcheck", "Id": "kdevcppcheck", "License": "GPL", "Name": "Cppcheck Support", "Name[ca@valencia]": "Implementació del Cppcheck", "Name[ca]": "Implementació del Cppcheck", "Name[cs]": "Podpora Cppcheck", "Name[de]": "Cppcheck-Unterstützung", - "Name[el]": "Υποστήριξη cppcheck", "Name[en_GB]": "Cppcheck Support", "Name[es]": "Implementación de Cppcheck", - "Name[et]": "Cppchecki toetus", "Name[fi]": "Cppcheck-tuki", "Name[fr]": "Prise en charge de Cppcheck", "Name[gl]": "Compatibilidade con Cppcheck.", "Name[it]": "Supporto per Cppcheck", - "Name[nl]": "Ondersteuning van Cppcheck", + "Name[nl]": "Ondersteuning van cppcheck", "Name[pl]": "Obsługa Cppcheck", "Name[pt]": "Suporte para o Cppcheck", "Name[pt_BR]": "Suporte ao Cppcheck", "Name[sk]": "Podpora Cppcheck", "Name[sl]": "Podpora za Cppcheck", "Name[sv]": "Cppcheck-stöd", "Name[tr]": "Cppcheck Desteği", "Name[uk]": "Підтримка Cppcheck", "Name[x-test]": "xxCppcheck Supportxx", "Name[zh_CN]": "Cppcheck 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/custom-buildsystem/kdevcustombuildsystem.json b/plugins/custom-buildsystem/kdevcustombuildsystem.json index 94104ba15d..b13bfd88af 100644 --- a/plugins/custom-buildsystem/kdevcustombuildsystem.json +++ b/plugins/custom-buildsystem/kdevcustombuildsystem.json @@ -1,66 +1,61 @@ { "KPlugin": { "Description": "Allows to use custom buildsystems for KDevelop projects", - "Description[bs]": "DOpušta korištene vlastitih sistema za gradnju KDevelop projekata", "Description[ca@valencia]": "Permet utilitzar sistemes de construcció personalitzats per a projectes del KDevelop", "Description[ca]": "Permet utilitzar sistemes de construcció personalitzats per a projectes del KDevelop", "Description[de]": "Ermöglicht ein benutzerdefiniertes Erstellungssystem für KDevelop-Projekte", - "Description[el]": "Επιτρέπει τη χρήση προσαρμοσμένων συστημάτων κατασκευής για έργα του KDevelop", "Description[en_GB]": "Allows to use custom build systems for KDevelop projects", "Description[es]": "Permite el uso de sistemas de compilación personalizados en proyectos de KDevelop", - "Description[et]": "Võimaldab kasutada KDevelopi projektides kohandatud ehitamissüsteeme.", + "Description[et]": "Võimaldab kasutada KDevelopi projektide puhul kohandatud ehitamissüsteemi", "Description[fi]": "Mahdollistaa omien käännösjärjestelmien käytön KDevelop-projekteissa", "Description[fr]": "Permet d'utiliser des systèmes de construction personnalisés pour des projets KDevelop", "Description[gl]": "Permite empregar sistemas de construción personalizados cos proxectos de KDevelop.", "Description[it]": "Permette di usare buildsystem personalizzati per i progetti di KDevelop", "Description[nl]": "Staat toe eigengemaakte bouwsystemen voor projecten van KDevelop te gebruiken", "Description[pl]": "Umożliwia stosowanie własnych systemów budowania dla projektów KDevelop", "Description[pt]": "Permite usar sistemas de compilação personalizados para projectos do KDevelop", "Description[pt_BR]": "Permite usar sistemas de compilação personalizados para projetos do KDevelop", "Description[sk]": "Povolí použiť vlastné buildovacie systémy pre projekty KDevelop", "Description[sl]": "Dovoli uporabo sistemov za izgradnjo po meri za projekte KDevelop", "Description[sv]": "Gör det möjligt att använda egna byggsystem för projekt i KDevelop", "Description[tr]": "KDevelop projeleri için özel inşa sistemlerinin kullanılmasına izin verir", "Description[uk]": "Надає змогу використовувати нетипові системи збирання у проєктах KDevelop", "Description[x-test]": "xxAllows to use custom buildsystems for KDevelop projectsxx", "Description[zh_CN]": "允许对 KDevelop 工程使用自定义构建系统", "Icon": "kdevelop", "Id": "KDevCustomBuildSystem", "Name": "Custom Build System", "Name[ca@valencia]": "Sistema de construcció personalitzat", "Name[ca]": "Sistema de construcció personalitzat", "Name[cs]": "Vlastní systém pro sestavení", "Name[de]": "Eigenes Build-System", - "Name[el]": "Προσαρμοσμένο σύστημα κατασκευής", "Name[en_GB]": "Custom Build System", "Name[es]": "Sistema de construcción personalizado", - "Name[et]": "Kohandatud ehitamissüsteem", "Name[fr]": "Système personnalisé de compilation", "Name[gl]": "Sistema de construción personalizado", "Name[it]": "Sistema di compilazione personalizzato", "Name[nl]": "Zelf gebouwd systeem", "Name[pl]": "Własny system budowania", "Name[pt]": "Sistema de Compilação Personalizado", "Name[pt_BR]": "Sistema de compilação personalizado", "Name[sk]": "Vlastný zostavovací systém", - "Name[sl]": "Sistem za izgradnjo po meri", "Name[sv]": "Eget byggsystem", "Name[tr]": "Özel İnşa Sistemi", "Name[uk]": "Нетипова система збирання", "Name[x-test]": "xxCustom Build Systemxx", "Name[zh_CN]": "自定义构建系统", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectBuilder", "org.kdevelop.IProjectFileManager", "org.kdevelop.IBuildSystemManager" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp index 05fc6c9dee..e1e74816b6 100644 --- a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp @@ -1,330 +1,330 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "compilerprovider.h" #include "debug.h" #include "compilerfactories.h" #include "settingsmanager.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { class NoCompiler : public ICompiler { public: NoCompiler(): ICompiler(i18n("None"), QString(), QString(), false) {} QHash< QString, QString > defines(Utils::LanguageType, const QString&) const override { return {}; } Path::List includes(Utils::LanguageType, const QString&) const override { return {}; } }; static CompilerPointer createDummyCompiler() { static CompilerPointer compiler(new NoCompiler()); return compiler; } ConfigEntry configForItem(KDevelop::ProjectBaseItem* item) { if(!item){ return ConfigEntry(); } const Path itemPath = item->path(); const Path rootDirectory = item->project()->path(); - auto paths = SettingsManager::globalInstance()->readPaths(item->project()->projectConfiguration().data()); + const auto paths = SettingsManager::globalInstance()->readPaths(item->project()->projectConfiguration().data()); ConfigEntry config; Path closestPath; // find config entry closest to the requested item for (const auto& entry : paths) { auto configEntry = entry; Path targetDirectory = rootDirectory; targetDirectory.addPath(entry.path); if (targetDirectory == itemPath) { return configEntry; } if (targetDirectory.isParentOf(itemPath)) { if (config.path.isEmpty() || targetDirectory.segments().size() > closestPath.segments().size()) { config = configEntry; closestPath = targetDirectory; } } } return config; } } ProjectTargetItem* findCompiledTarget(ProjectBaseItem* item) { const auto targets = item->targetList(); for (auto* item : targets) { if (item->type() == ProjectBaseItem::ExecutableTarget || item->type() == ProjectBaseItem::LibraryTarget) { return item; } } const auto folders = item->folderList(); for (auto* folder: folders) { auto target = findCompiledTarget(folder); if (target) return target; } return nullptr; } CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent ) : QObject( parent ) , m_settings(settings) { m_factories = { CompilerFactoryPointer(new GccFactory()), CompilerFactoryPointer(new ClangFactory()), #ifdef _WIN32 CompilerFactoryPointer(new MsvcFactory()), #endif }; if (!QStandardPaths::findExecutable( QStringLiteral("clang") ).isEmpty()) { m_factories[1]->registerDefaultCompilers(this); } if (!QStandardPaths::findExecutable( QStringLiteral("gcc") ).isEmpty()) { m_factories[0]->registerDefaultCompilers(this); } #ifdef _WIN32 if (!QStandardPaths::findExecutable(QStringLiteral("cl.exe")).isEmpty()) { m_factories[2]->registerDefaultCompilers(this); } #endif registerCompiler(createDummyCompiler()); retrieveUserDefinedCompilers(); connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, [this]() { m_defaultProvider.clear(); }); connect(ICore::self()->projectController(), &IProjectController::projectConfigurationChanged, this, &CompilerProvider::projectChanged); connect(ICore::self()->projectController(), &IProjectController::projectOpened, this, &CompilerProvider::projectChanged); } CompilerProvider::~CompilerProvider() = default; void CompilerProvider::projectChanged(KDevelop::IProject* p) { const auto target = findCompiledTarget(p->projectItem()); if (!target) return; auto path = p->buildSystemManager()->compiler(target); qCDebug(DEFINESANDINCLUDES) << "found compiler" << path; if (path.isEmpty()) return; Q_ASSERT(QDir::isAbsolutePath(path.toLocalFile())); const auto pathString = path.toLocalFile(); auto it = std::find_if(m_compilers.begin(), m_compilers.end(), [&pathString](const CompilerPointer& compiler) { return compiler->path() == pathString; }); if (it != m_compilers.end()) { m_defaultProvider = *it; return; } //we need to search, sdk compiler names are weird: arm-linux-androideabi-g++ for (auto& factory : qAsConst(m_factories)) { if (factory->isSupported(path)) { auto compiler = factory->createCompiler(path.lastPathSegment(), pathString); registerCompiler(compiler); m_defaultProvider = compiler; } } qCDebug(DEFINESANDINCLUDES) << "using compiler" << m_defaultProvider << path; } QHash CompilerProvider::defines( const QString& path ) const { auto config = configForItem(nullptr); auto languageType = Utils::languageType(path, config.parserArguments.parseAmbiguousAsCPP); // If called on files that we can't compile, return an empty set of defines. if (languageType == Utils::Other) { return {}; } return config.compiler->defines(languageType, config.parserArguments[languageType]); } QHash CompilerProvider::defines( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { languageType = Utils::languageType(item->path().path(), config.parserArguments.parseAmbiguousAsCPP); } // If called on files that we can't compile, return an empty set of defines. if (languageType == Utils::Other) { return {}; } return config.compiler->defines(languageType, config.parserArguments[languageType]); } Path::List CompilerProvider::includes( const QString& path ) const { auto config = configForItem(nullptr); auto languageType = Utils::languageType(path, config.parserArguments.parseAmbiguousAsCPP); // If called on files that we can't compile, return an empty set of includes. if (languageType == Utils::Other) { return {}; } return config.compiler->includes(languageType, config.parserArguments[languageType]); } Path::List CompilerProvider::includes( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { languageType = Utils::languageType(item->path().path(), config.parserArguments.parseAmbiguousAsCPP); } // If called on files that we can't compile, return an empty set of includes. if (languageType == Utils::Other) { return {}; } return config.compiler->includes(languageType, config.parserArguments[languageType]); } Path::List CompilerProvider::frameworkDirectories( const QString& /* path */ ) const { return {}; } Path::List CompilerProvider::frameworkDirectories( ProjectBaseItem* /* item */ ) const { return {}; } IDefinesAndIncludesManager::Type CompilerProvider::type() const { return IDefinesAndIncludesManager::CompilerSpecific; } CompilerPointer CompilerProvider::defaultCompiler() const { if (m_defaultProvider) return m_defaultProvider; auto rt = ICore::self()->runtimeController()->currentRuntime(); for ( const CompilerPointer& compiler : m_compilers ) { if (rt->findExecutable(compiler->path()).isEmpty()) continue; m_defaultProvider = compiler; break; } if (!m_defaultProvider) m_defaultProvider = createDummyCompiler(); qCDebug(DEFINESANDINCLUDES) << "new default compiler" << rt->name() << m_defaultProvider->name() << m_defaultProvider->path(); return m_defaultProvider; } QVector< CompilerPointer > CompilerProvider::compilers() const { return m_compilers; } CompilerPointer CompilerProvider::compilerForItem( KDevelop::ProjectBaseItem* item ) const { auto compiler = configForItem(item).compiler; Q_ASSERT(compiler); return compiler; } bool CompilerProvider::registerCompiler(const CompilerPointer& compiler) { if (!compiler) { return false; } for (auto& c : qAsConst(m_compilers)) { if (c->name() == compiler->name()) { return false; } } m_compilers.append(compiler); return true; } void CompilerProvider::unregisterCompiler(const CompilerPointer& compiler) { if (!compiler->editable()) { return; } for (int i = 0; i < m_compilers.count(); i++) { if (m_compilers[i]->name() == compiler->name()) { m_compilers.remove(i); break; } } } QVector< CompilerFactoryPointer > CompilerProvider::compilerFactories() const { return m_factories; } void CompilerProvider::retrieveUserDefinedCompilers() { const auto compilers = m_settings->userDefinedCompilers(); for (auto& c : compilers) { registerCompiler(c); } } diff --git a/plugins/custom-definesandincludes/includepathsconverter.cpp b/plugins/custom-definesandincludes/includepathsconverter.cpp index 2be3d71eca..7cca756d0f 100644 --- a/plugins/custom-definesandincludes/includepathsconverter.cpp +++ b/plugins/custom-definesandincludes/includepathsconverter.cpp @@ -1,246 +1,246 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "includepathsconverter.h" #include #include #include #include #include #include #include #include "settingsmanager.h" using namespace KDevelop; namespace { KSharedConfigPtr openConfigFile(const QString& configFile) { return KSharedConfig::openConfig(configFile, KConfig::SimpleConfig); } QString findconfigFile(const QString& projectDir) { QDirIterator dirIterator(projectDir + QLatin1String("/.kdev4")); while (dirIterator.hasNext()) { dirIterator.next(); if (dirIterator.fileName().endsWith(QLatin1String(".kdev4"))) { return dirIterator.fileInfo().canonicalFilePath(); } } return {}; } QString findProject(const QString& subdirectory) { QDir project(subdirectory); do { if (project.exists(QStringLiteral(".kdev4"))) { return project.path(); } } while(project.cdUp()); return {}; } } IncludePathsConverter::IncludePathsConverter() { } bool IncludePathsConverter::addIncludePaths(const QStringList& includeDirectories, const QString& projectConfigFile, const QString& subdirectory) { auto configFile = openConfigFile(projectConfigFile); if (!configFile) { return false; } auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); QString path = subdirectory.isEmpty() ? QStringLiteral(".") : subdirectory; ConfigEntry config; for (auto& entry: configEntries) { if (path == entry.path) { config = entry; config.includes += includeDirectories; config.includes.removeDuplicates(); entry = config; break; } } if (config.path.isEmpty()) { config.path = path; config.includes = includeDirectories; configEntries.append(config); } SettingsManager::globalInstance()->writePaths(configFile.data(), configEntries); return true; } bool IncludePathsConverter::removeIncludePaths(const QStringList& includeDirectories, const QString& projectConfigFile, const QString& subdirectory) { auto configFile = openConfigFile(projectConfigFile); if (!configFile) { return false; } auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); QString path = subdirectory.isEmpty() ? QStringLiteral(".") : subdirectory; for (auto& entry: configEntries) { if (path == entry.path) { for(const auto& include: includeDirectories) { entry.includes.removeAll(include); } SettingsManager::globalInstance()->writePaths(configFile.data(), configEntries); return true; } } return true; } QStringList IncludePathsConverter::readIncludePaths(const QString& projectConfigFile, const QString& subdirectory) const { auto configFile = openConfigFile(projectConfigFile); if (!configFile) { return {}; } QString path = subdirectory.isEmpty() ? QStringLiteral(".") : subdirectory; - auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); + const auto configEntries = SettingsManager::globalInstance()->readPaths(configFile.data()); for (const auto& entry: configEntries) { if (path == entry.path) { return entry.includes; } } return {}; } int main(int argc, char** argv) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral("kdev_includepathsconverter")); QCommandLineParser parser; parser.setApplicationDescription(QStringLiteral("\nAdds, removes or shows include directories of a project. Also it can be used as a tool to convert include directories from .kdev_include_paths file to the new format.\n\n" "Examples:\ncat /project/path/.kdev_include_paths | xargs -d '\\n' kdev_includepathsconverter -a /project/path/\n\n" "kdev_includepathsconverter -r /project/path/subdirectory/ \"/some/include/dir\" \"/another/include/dir\" \n\n" "kdev_includepathsconverter -l /project/path/another/subdirectory/")); parser.addHelpOption(); QCommandLineOption listOption(QStringLiteral("l"), QCoreApplication::translate("main", "Shows include directories used by the project"), QCoreApplication::translate("main", "project")); parser.addOption(listOption); QCommandLineOption addOption(QStringLiteral("a"), QCoreApplication::translate("main", "Adds include directories to the project"), QCoreApplication::translate("main", "project")); parser.addOption(addOption); QCommandLineOption removeOption(QStringLiteral("r"), QCoreApplication::translate("main", "Removes include directories from the project"), QCoreApplication::translate("main", "project")); parser.addOption(removeOption); parser.process(app); QString projectDir; QStringList includeDirectories(parser.positionalArguments()); std::transform(includeDirectories.begin(), includeDirectories.end(), includeDirectories.begin(), [](const QString& path) { return path.trimmed(); } ); includeDirectories.erase(std::remove_if(includeDirectories.begin(), includeDirectories.end(), [](const QString& path) { return path.isEmpty(); } ), includeDirectories.end()); bool show = parser.isSet(listOption); bool add = parser.isSet(addOption); bool remove = parser.isSet(removeOption); if (show) { projectDir = parser.value(listOption); } else if(add) { projectDir = parser.value(addOption); } else if(remove) { projectDir = parser.value(removeOption); } if (projectDir.isEmpty()) { parser.showHelp(-1); } QString subdirectory = projectDir; projectDir = findProject(projectDir); QString configFile = findconfigFile(projectDir); QTextStream out(stdout); if (configFile.isEmpty()) { out << QCoreApplication::translate("main", "No project found for: ") << subdirectory; return -1; } if (add || remove) { if (includeDirectories.isEmpty()) { parser.showHelp(-1); } } { auto subdirCanonical = QFileInfo(subdirectory).canonicalFilePath(); auto projectCanonical = QFileInfo(projectDir).canonicalFilePath(); if (subdirCanonical != projectCanonical) { subdirectory = subdirCanonical.mid(projectCanonical.size()); if (subdirectory.startsWith(QLatin1Char('/'))) { subdirectory.remove(0,1); } } else { subdirectory.clear(); } } IncludePathsConverter converter; if (remove) { if (!converter.removeIncludePaths(includeDirectories, configFile, subdirectory)) { out << QCoreApplication::translate("main", "Can't remove include paths"); } } if (add) { if (!converter.addIncludePaths(includeDirectories, configFile, subdirectory)) { out << QCoreApplication::translate("main", "Can't add include paths"); } } if (show) { const auto& includes = converter.readIncludePaths(configFile, subdirectory); for (const auto& include : includes) { out << include << "\n"; } } } diff --git a/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp b/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp index bbd10aeec1..ea4b0d4346 100644 --- a/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp +++ b/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp @@ -1,220 +1,236 @@ /* * This file is part of KDevelop * * Copyright 2015 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "parserwidget.h" #include "ui_parserwidget.h" #include "compilerprovider/settingsmanager.h" #include namespace { QString languageStandard(const QString& arguments) { int idx = arguments.indexOf(QLatin1String("-std=")); if(idx == -1){ return QStringLiteral("c++11"); } idx += 5; int end = arguments.indexOf(QLatin1Char(' '), idx) != -1 ? arguments.indexOf(QLatin1Char(' '), idx) : arguments.size(); return arguments.mid(idx, end - idx); } QString languageDefaultStandard(Utils::LanguageType languageType) { switch (languageType) { case Utils::C: return QStringLiteral("c99"); case Utils::Cpp: return QStringLiteral("c++11"); case Utils::OpenCl: return QStringLiteral("CL1.1"); case Utils::Cuda: return QStringLiteral("c++11"); case Utils::ObjC: return QStringLiteral("c99"); case Utils::ObjCpp: return QStringLiteral("c++11"); case Utils::Other: break; } Q_UNREACHABLE(); } bool isCustomParserArguments(Utils::LanguageType languageType, const QString& arguments, const QStringList& standards) { const auto defaultArguments = SettingsManager::globalInstance()->defaultParserArguments(); auto standard = languageStandard(arguments); auto tmpArgs(arguments); tmpArgs.replace(standard, languageDefaultStandard(languageType)); if (tmpArgs == defaultArguments[languageType] && standards.contains(standard)) { return false; } return true; } const int customProfileIdx = 0; } ParserWidget::ParserWidget(QWidget* parent) : QWidget(parent) , m_ui(new Ui::ParserWidget()) { m_ui->setupUi(this); connect(m_ui->parserOptionsC, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsCpp, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsOpenCl, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsCuda, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parseHeadersInPlainC, &QCheckBox::stateChanged, this, &ParserWidget::textEdited); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect(m_ui->languageStandardsC, &QComboBox::textActivated, +#else connect(m_ui->languageStandardsC, QOverload::of(&QComboBox::activated), +#endif this, &ParserWidget::languageStandardChangedC); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect(m_ui->languageStandardsCpp, &QComboBox::textActivated, +#else connect(m_ui->languageStandardsCpp, QOverload::of(&QComboBox::activated), +#endif this, &ParserWidget::languageStandardChangedCpp); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect(m_ui->languageStandardsOpenCl, &QComboBox::textActivated, +#else connect(m_ui->languageStandardsOpenCl, QOverload::of(&QComboBox::activated), +#endif this, &ParserWidget::languageStandardChangedOpenCl); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect(m_ui->languageStandardsCuda, &QComboBox::textActivated, +#else connect(m_ui->languageStandardsCuda, QOverload::of(&QComboBox::activated), +#endif this, &ParserWidget::languageStandardChangedCuda); updateEnablements(); } ParserWidget::~ParserWidget() = default; void ParserWidget::textEdited() { emit changed(); } void ParserWidget::languageStandardChangedC(const QString& standard) { if (m_ui->languageStandardsC->currentIndex() == customProfileIdx) { m_ui->parserOptionsC->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::C]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::C]; auto currentStandard = languageStandard(text); m_ui->parserOptionsC->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedCpp(const QString& standard) { if (m_ui->languageStandardsCpp->currentIndex() == customProfileIdx) { m_ui->parserOptionsCpp->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cpp]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cpp]; auto currentStandard = languageStandard(text); m_ui->parserOptionsCpp->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedOpenCl(const QString& standard) { if (m_ui->languageStandardsOpenCl->currentIndex() == customProfileIdx) { m_ui->parserOptionsOpenCl->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::OpenCl]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::OpenCl]; auto currentStandard = languageStandard(text); m_ui->parserOptionsOpenCl->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedCuda(const QString& standard) { if (m_ui->languageStandardsCuda->currentIndex() == customProfileIdx) { m_ui->parserOptionsCuda->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cuda]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cuda]; auto currentStandard = languageStandard(text); m_ui->parserOptionsCuda->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::setParserArguments(const ParserArguments& arguments) { auto setArguments = [arguments](QComboBox* languageStandards, QLineEdit* parserOptions, Utils::LanguageType languageType) { QStringList standards; const int languageStandardsCount = languageStandards->count(); standards.reserve(languageStandardsCount-1); for (int i = 1; i < languageStandardsCount; ++i) { standards << languageStandards->itemText(i); } const QString& arg = arguments[languageType]; if (isCustomParserArguments(languageType, arg, standards)) { languageStandards->setCurrentIndex(customProfileIdx); } else { languageStandards->setCurrentText(languageStandard(arg)); } parserOptions->setText(arg); }; setArguments(m_ui->languageStandardsCpp, m_ui->parserOptionsCpp, Utils::Cpp); setArguments(m_ui->languageStandardsC, m_ui->parserOptionsC, Utils::C); setArguments(m_ui->languageStandardsOpenCl, m_ui->parserOptionsOpenCl, Utils::OpenCl); setArguments(m_ui->languageStandardsCuda, m_ui->parserOptionsCuda, Utils::Cuda); m_ui->parseHeadersInPlainC->setChecked(!arguments.parseAmbiguousAsCPP); updateEnablements(); } ParserArguments ParserWidget::parserArguments() const { ParserArguments arguments; arguments[Utils::C] = m_ui->parserOptionsC->text(); arguments[Utils::Cpp] = m_ui->parserOptionsCpp->text(); arguments[Utils::OpenCl] = m_ui->parserOptionsOpenCl->text(); arguments[Utils::Cuda] = m_ui->parserOptionsCuda->text(); arguments.parseAmbiguousAsCPP = !m_ui->parseHeadersInPlainC->isChecked(); return arguments; } void ParserWidget::updateEnablements() { m_ui->parserOptionsCpp->setEnabled(m_ui->languageStandardsCpp->currentIndex() == customProfileIdx); m_ui->parserOptionsC->setEnabled(m_ui->languageStandardsC->currentIndex() == customProfileIdx); m_ui->parserOptionsOpenCl->setEnabled(m_ui->languageStandardsOpenCl->currentIndex() == customProfileIdx); m_ui->parserOptionsCuda->setEnabled(m_ui->languageStandardsCuda->currentIndex() == customProfileIdx); } diff --git a/plugins/custom-definesandincludes/kcm_widget/projectpathswidget.cpp b/plugins/custom-definesandincludes/kcm_widget/projectpathswidget.cpp index d7f87e05b9..1e83a937cc 100644 --- a/plugins/custom-definesandincludes/kcm_widget/projectpathswidget.cpp +++ b/plugins/custom-definesandincludes/kcm_widget/projectpathswidget.cpp @@ -1,311 +1,321 @@ /************************************************************************ * * * Copyright 2010 Andreas Pakulat * * Copyright 2014 Sergey Kalinichev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ************************************************************************/ #include "projectpathswidget.h" #include #include #include #include #include "../compilerprovider/compilerprovider.h" #include "../compilerprovider/settingsmanager.h" #include "ui_projectpathswidget.h" #include "ui_batchedit.h" #include "projectpathsmodel.h" #include #include #include using namespace KDevelop; namespace { enum PageType { IncludesPage, DefinesPage, ParserArgumentsPage }; } ProjectPathsWidget::ProjectPathsWidget( QWidget* parent ) : QWidget(parent), ui(new Ui::ProjectPathsWidget), pathsModel(new ProjectPathsModel(this)) { ui->setupUi( this ); // hack taken from kurlrequester, make the buttons a bit less in height so they better match the url-requester ui->addPath->setFixedHeight( ui->projectPaths->sizeHint().height() ); ui->removePath->setFixedHeight( ui->projectPaths->sizeHint().height() ); connect( ui->addPath, &QPushButton::clicked, this, &ProjectPathsWidget::addProjectPath ); connect( ui->removePath, &QPushButton::clicked, this, &ProjectPathsWidget::deleteProjectPath ); connect( ui->batchEdit, &QPushButton::clicked, this, &ProjectPathsWidget::batchEdit ); ui->projectPaths->setModel( pathsModel ); connect( ui->projectPaths, QOverload::of(&KComboBox::currentIndexChanged), this, &ProjectPathsWidget::projectPathSelected ); connect( pathsModel, &ProjectPathsModel::dataChanged, this, &ProjectPathsWidget::changed ); connect( pathsModel, &ProjectPathsModel::rowsInserted, this, &ProjectPathsWidget::changed ); connect( pathsModel, &ProjectPathsModel::rowsRemoved, this, &ProjectPathsWidget::changed ); - connect( ui->compiler, QOverload::of(&QComboBox::activated), this, &ProjectPathsWidget::changed ); - connect( ui->compiler, QOverload::of(&QComboBox::activated), this, &ProjectPathsWidget::changeCompilerForPath ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect( ui->compiler, &QComboBox::textActivated, +#else + connect( ui->compiler, QOverload::of(&QComboBox::activated), +#endif + this, &ProjectPathsWidget::changed ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect( ui->compiler, &QComboBox::textActivated, +#else + connect( ui->compiler, QOverload::of(&QComboBox::activated), +#endif + this, &ProjectPathsWidget::changeCompilerForPath ); connect( ui->includesWidget, QOverload::of(&IncludesWidget::includesChanged), this, &ProjectPathsWidget::includesChanged ); connect( ui->definesWidget, QOverload::of(&DefinesWidget::definesChanged), this, &ProjectPathsWidget::definesChanged ); connect(ui->languageParameters, &QTabWidget::currentChanged, this, &ProjectPathsWidget::tabChanged); connect(ui->parserWidget, &ParserWidget::changed, this, &ProjectPathsWidget::parserArgumentsChanged); tabChanged(IncludesPage); } ProjectPathsWidget::~ProjectPathsWidget() { } QVector ProjectPathsWidget::paths() const { return pathsModel->paths(); } void ProjectPathsWidget::setPaths( const QVector& paths ) { bool b = blockSignals( true ); clear(); pathsModel->setPaths( paths ); blockSignals( b ); ui->projectPaths->setCurrentIndex(0); // at least a project root item is present ui->languageParameters->setCurrentIndex(0); // Set compilers ui->compiler->clear(); auto settings = SettingsManager::globalInstance(); auto compilers = settings->provider()->compilers(); for (int i = 0 ; i < compilers.count(); ++i) { Q_ASSERT(compilers[i]); if (!compilers[i]) { continue; } ui->compiler->addItem(compilers[i]->name()); QVariant val; val.setValue(compilers[i]); ui->compiler->setItemData(i, val); } projectPathSelected(0); updateEnablements(); } void ProjectPathsWidget::definesChanged( const Defines& defines ) { qCDebug(DEFINESANDINCLUDES) << "defines changed"; updatePathsModel( QVariant::fromValue(defines), ProjectPathsModel::DefinesDataRole ); } void ProjectPathsWidget::includesChanged( const QStringList& includes ) { qCDebug(DEFINESANDINCLUDES) << "includes changed"; updatePathsModel( includes, ProjectPathsModel::IncludesDataRole ); } void ProjectPathsWidget::parserArgumentsChanged() { updatePathsModel(QVariant::fromValue(ui->parserWidget->parserArguments()), ProjectPathsModel::ParserArgumentsRole); } void ProjectPathsWidget::updatePathsModel(const QVariant& newData, int role) { QModelIndex idx = pathsModel->index( ui->projectPaths->currentIndex(), 0, QModelIndex() ); if( idx.isValid() ) { bool b = pathsModel->setData( idx, newData, role ); if( b ) { emit changed(); } } } void ProjectPathsWidget::projectPathSelected( int index ) { if( index < 0 && pathsModel->rowCount() > 0 ) { index = 0; } Q_ASSERT(index >= 0); const QModelIndex midx = pathsModel->index( index, 0 ); ui->includesWidget->setIncludes( pathsModel->data( midx, ProjectPathsModel::IncludesDataRole ).toStringList() ); ui->definesWidget->setDefines( pathsModel->data( midx, ProjectPathsModel::DefinesDataRole ).value() ); Q_ASSERT(pathsModel->data(midx, ProjectPathsModel::CompilerDataRole).value()); ui->compiler->setCurrentText(pathsModel->data(midx, ProjectPathsModel::CompilerDataRole).value()->name()); ui->parserWidget->setParserArguments(pathsModel->data(midx, ProjectPathsModel::ParserArgumentsRole).value()); updateEnablements(); } void ProjectPathsWidget::clear() { bool sigDisabled = ui->projectPaths->blockSignals( true ); pathsModel->setPaths({}); ui->includesWidget->clear(); ui->definesWidget->clear(); updateEnablements(); ui->projectPaths->blockSignals( sigDisabled ); } void ProjectPathsWidget::addProjectPath() { const QUrl directory = pathsModel->data(pathsModel->index(0, 0), ProjectPathsModel::FullUrlDataRole).toUrl(); QPointer dlg = new QFileDialog(this, i18n("Select Project Path"), directory.toLocalFile()); dlg->setFileMode(QFileDialog::Directory); dlg->setOption(QFileDialog::ShowDirsOnly); if (dlg->exec()) { pathsModel->addPath(dlg->selectedUrls().value(0)); ui->projectPaths->setCurrentIndex(pathsModel->rowCount() - 1); updateEnablements(); } delete dlg; } void ProjectPathsWidget::deleteProjectPath() { const QModelIndex idx = pathsModel->index( ui->projectPaths->currentIndex(), 0 ); if( KMessageBox::questionYesNo( this, i18n("Are you sure you want to remove the configuration for the path '%1'?", pathsModel->data( idx, Qt::DisplayRole ).toString() ), QStringLiteral("Remove Path Configuration") ) == KMessageBox::Yes ) { pathsModel->removeRows( ui->projectPaths->currentIndex(), 1 ); } updateEnablements(); } void ProjectPathsWidget::setProject(KDevelop::IProject* w_project) { pathsModel->setProject( w_project ); ui->includesWidget->setProject( w_project ); } void ProjectPathsWidget::updateEnablements() { // Disable removal of the project root entry which is always first in the list ui->removePath->setEnabled( ui->projectPaths->currentIndex() > 0 ); } void ProjectPathsWidget::batchEdit() { Ui::BatchEdit be; QPointer dialog = new QDialog(this); be.setupUi(dialog); const int index = qMax(ui->projectPaths->currentIndex(), 0); const QModelIndex midx = pathsModel->index(index, 0); if (!midx.isValid()) { return; } bool includesTab = ui->languageParameters->currentIndex() == 0; if (includesTab) { auto includes = pathsModel->data(midx, ProjectPathsModel::IncludesDataRole).toStringList(); be.textEdit->setPlainText(includes.join(QLatin1Char('\n'))); dialog->setWindowTitle(i18n("Edit include directories/files")); } else { auto defines = pathsModel->data(midx, ProjectPathsModel::DefinesDataRole).value(); for (auto it = defines.constBegin(); it != defines.constEnd(); it++) { be.textEdit->appendPlainText(it.key() + QLatin1Char('=') + it.value()); } dialog->setWindowTitle(i18n("Edit defined macros")); } if (dialog->exec() != QDialog::Accepted) { delete dialog; return; } if (includesTab) { auto includes = be.textEdit->toPlainText().split(QLatin1Char('\n'), QString::SkipEmptyParts); for (auto& s : includes) { s = s.trimmed(); } pathsModel->setData(midx, includes, ProjectPathsModel::IncludesDataRole); } else { auto list = be.textEdit->toPlainText().split(QLatin1Char('\n'), QString::SkipEmptyParts); Defines defines; for (auto& d : list) { //This matches: a=b, a=, a QRegExp r(QStringLiteral("^([^=]+)(=(.*))?$")); if (!r.exactMatch(d)) { continue; } defines[r.cap(1).trimmed()] = r.cap(3).trimmed(); } pathsModel->setData(midx, QVariant::fromValue(defines), ProjectPathsModel::DefinesDataRole); } projectPathSelected(index); delete dialog; } void ProjectPathsWidget::setCurrentCompiler(const QString& name) { for (int i = 0 ; i < ui->compiler->count(); ++i) { if(ui->compiler->itemText(i) == name) { ui->compiler->setCurrentIndex(i); } } } CompilerPointer ProjectPathsWidget::currentCompiler() const { return ui->compiler->itemData(ui->compiler->currentIndex()).value(); } void ProjectPathsWidget::tabChanged(int idx) { if (idx == ParserArgumentsPage) { ui->batchEdit->setVisible(false); ui->compilerBox->setVisible(true); ui->configureLabel->setText(i18n("Configure C/C++ parser")); } else { ui->batchEdit->setVisible(true); ui->compilerBox->setVisible(false); ui->configureLabel->setText(i18n("Configure which macros and include directories/files will be added to the parser during project parsing:")); } } void ProjectPathsWidget::changeCompilerForPath() { for (int idx = 0; idx < pathsModel->rowCount(); idx++) { const QModelIndex midx = pathsModel->index(idx, 0); if (pathsModel->data(midx, Qt::DisplayRole) == ui->projectPaths->currentText()) { pathsModel->setData(midx, QVariant::fromValue(currentCompiler()), ProjectPathsModel::CompilerDataRole); break; } } } diff --git a/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json b/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json index 7b75528d29..f5668c9b18 100644 --- a/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json +++ b/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json @@ -1,62 +1,59 @@ { "KPlugin": { "Category": "Project Management", "Description": "Configure which macros and include directories/files will be added to the parser during project parsing.", - "Description[ar]": "اضبط أيّ الماكروهات والملفّات/الأدلّة المضمّنة ستُضاف إلى المحلّل أثناء تحليل المشروع.", "Description[ca@valencia]": "Configura quines macros i directoris/fitxers s'afegiran a l'analitzador durant l'anàlisi del projecte.", "Description[ca]": "Configura quines macros i directoris/fitxers s'afegiran a l'analitzador durant l'anàlisi del projecte.", "Description[de]": "Einstellung der Makros und Include-Ordner/-Dateien, die für den Parser während des Einlesens des Projekts hinzugefügt werden.", - "Description[el]": "Διαμορφώνει τις μακροεντολές και τους include καταλόγους/αρχεία που θα προστεθούν στον αναλυτή κατά την ανάλυση του έργου.", "Description[en_GB]": "Configure which macros and include directories/files will be added to the parser during project parsing.", "Description[es]": "Configurar las macros y los directorios/archivos a incluir que se añadirán al analizador sintáctico durante el análisis de los proyectos.", - "Description[et]": "Seadistamine, milliseid makrosid ja kaasatavaid katalooge/faile lisada parserile projekti parsimisel.", + "Description[et]": "Aitab seadistada, millised makrod ja päistekataloogid või -failid lisada parserile projekti parsimiseks.", "Description[fi]": "Määritä, mitkä makrot ja include-hakemistot/tiedostot lisätään jäsentimelle projektin jäsentämisen aikana.", "Description[fr]": "Configurer quelles macros et dossiers / fichiers à inclure seront ajoutés pour l'analyseur pendant l'analyse du projet.", "Description[gl]": "Configure que macros e ficheiros ou directorios de inclusión se engaden ao analizador do proxecto.", "Description[it]": "Configura quali macro e cartelle/file di inclusione saranno aggiunti all'analizzatore durante l'analisi del progetto.", "Description[nl]": "Stel in welke macro's en ingevoegde mappen/bestanden toegevoegd zullen worden aan de parser bij het ontleden van het project.", "Description[pl]": "Określa makra oraz pliki/katalogi dołączane uwzględniane przy przetwarzaniu projektu.", "Description[pt]": "Configura as macros e ficheiros/pastas de inclusão a adicionar ao processador, durante o processamento do projecto.", "Description[pt_BR]": "Configura quais macros e arquivos/pastas incluídos serão adicionados ao processador durante o processamento do projeto.", "Description[sk]": "Nastaviť, ktoré makrá a zahrnuté adresáre/súbory sa pridajú do spracovača počas spracovania projektu.", "Description[sl]": "Nastavi, kateri makri in mape/datoteke z vključitvami bodo dodane razčlenjevalniku med razčlenjevanjem projekta.", "Description[sv]": "Anpassa vilka makron och filer eller kataloger att inkludera som läggs till i tolken under projekttolkning.", "Description[tr]": "Hangi makroların ve dahil etme dizinlerinin/dosyaların ayrıştırma sürecinde ayrıştırıcıya ekleneceğini yapılandırın.", "Description[uk]": "Налаштувати макроси і включені каталоги або файли, які буде додано під час обробки проєкту.", "Description[x-test]": "xxConfigure which macros and include directories/files will be added to the parser during project parsing.xx", "Description[zh_CN]": "配置在解析工程时将添加哪些宏和包含目录/文件", "Icon": "kdevelop", "Id": "KDevDefinesAndIncludesManager", "Name": "Custom Defines And Includes Manager", "Name[ca@valencia]": "Gestor per personalitzar les definicions i inclusions", "Name[ca]": "Gestor per personalitzar les definicions i inclusions", "Name[de]": "Verwaltung benutzerdefinierter Definitionen und Includes", - "Name[el]": "Προσαρμοσμένος διαχειριστής defines και includes", "Name[en_GB]": "Custom Defines And Includes Manager", "Name[es]": "Gestor de definiciones e inclusiones personalizadas", - "Name[et]": "Kohandatud definitsioonide ja kaasatute haldur", + "Name[et]": "Kohandatud definitsioonide ja päiste haldur", "Name[fi]": "Mukautettujen definejen ja includejen hallinta", "Name[fr]": "Gestionnaire de définitions et inclusions personnalisées", "Name[gl]": "Xestor de definicións e inclusións personalizadas", "Name[it]": "Gestore delle definizioni e inclusioni personalizzate", "Name[nl]": "Beheerder van zelf gedefinieerd en invoegingen", "Name[pl]": "Zarządzanie własnymi definicjami i plikami dołączanymi", "Name[pt]": "Gestor de Definições e Inclusões Personalizadas", "Name[pt_BR]": "Gerenciador de definições e inclusões personalizadas", "Name[sk]": "Správca vlastných definícií a zahrnutí", "Name[sl]": "Upravljalnik določitev in vključitev po meri", "Name[sv]": "Hantering av egna definitioner och inkluderingar", "Name[tr]": "Özel Tanımlama ve İçerme Yöneticisi", "Name[uk]": "Керування нетиповими визначеннями і включеннями", "Name[x-test]": "xxCustom Defines And Includes Managerxx", "Name[zh_CN]": "自定义定义和包含管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IDefinesAndIncludesManager" ], "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/custom-definesandincludes/noprojectincludesanddefines/noprojectincludepathsmanager.cpp b/plugins/custom-definesandincludes/noprojectincludesanddefines/noprojectincludepathsmanager.cpp index e72d06f2cd..e0e6741f76 100644 --- a/plugins/custom-definesandincludes/noprojectincludesanddefines/noprojectincludepathsmanager.cpp +++ b/plugins/custom-definesandincludes/noprojectincludesanddefines/noprojectincludepathsmanager.cpp @@ -1,157 +1,157 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "noprojectincludepathsmanager.h" #include #include #include #include #include #include #include #include #include "noprojectcustomincludepaths.h" namespace { inline QString includePathsFile() { return QStringLiteral(".kdev_include_paths"); } bool removeSettings(const QString& storageDirectory) { const QString file = storageDirectory + QDir::separator() + includePathsFile(); return QFile::remove(file); } QStringList pathListToStringList(const Path::List& paths) { QStringList sl; sl.reserve(paths.size()); for (const auto& p : paths) { sl << p.path(); } return sl; } } QString NoProjectIncludePathsManager::findConfigurationFile(const QString& path) { QDir dir(path); while (dir.exists()) { QFileInfo customIncludePathsFile(dir, includePathsFile()); if (customIncludePathsFile.exists()) { return customIncludePathsFile.absoluteFilePath(); } if (!dir.cdUp()) { break; } } return {}; } std::pair> NoProjectIncludePathsManager::includesAndDefines(const QString& path) { QFileInfo fi(path); auto pathToFile = findConfigurationFile(fi.absoluteDir().absolutePath()); if (pathToFile.isEmpty()) { return {}; } Path::List includes; QHash defines; QFile f(pathToFile); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { - auto lines = QString::fromLocal8Bit(f.readAll()).split(QLatin1Char('\n'), QString::SkipEmptyParts); + const auto lines = QString::fromLocal8Bit(f.readAll()).split(QLatin1Char('\n'), QString::SkipEmptyParts); QFileInfo dir(pathToFile); const QChar dirSeparator = QDir::separator(); for (const auto& line : lines) { auto textLine = line.trimmed(); if (textLine.startsWith(QLatin1String("#define "))) { QStringList items = textLine.split(QLatin1Char(' ')); if (items.length() > 1) { defines[items[1]] = QStringList(items.mid(2)).join(QLatin1Char(' ')); }else{ qWarning() << i18n("Bad #define directive in %1: %2", pathToFile, textLine); } continue; } if (!textLine.isEmpty()) { QFileInfo pathInfo(textLine); if (pathInfo.isRelative()) { includes << Path(dir.canonicalPath() + dirSeparator + textLine); } else { includes << Path(textLine); } } } f.close(); } return std::make_pair(includes, defines); } bool NoProjectIncludePathsManager::writeIncludePaths(const QString& storageDirectory, const QStringList& includePaths) { QDir dir(storageDirectory); QFileInfo customIncludePaths(dir, includePathsFile()); QFile f(customIncludePaths.filePath()); if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { QTextStream out(&f); for (const auto& customPath : includePaths) { out << customPath << QLatin1Char('\n'); } if (includePaths.isEmpty()) { removeSettings(storageDirectory); } return true; } else { return false; } } void NoProjectIncludePathsManager::openConfigurationDialog(const QString& path) { auto cip = new NoProjectCustomIncludePaths; cip->setAttribute(Qt::WA_DeleteOnClose); cip->setModal(true); QFileInfo fi(path); auto dir = fi.absoluteDir().absolutePath(); cip->setStorageDirectory(dir); auto paths = includesAndDefines(path).first; cip->setCustomIncludePaths(pathListToStringList(paths)); QObject::connect(cip, &QDialog::accepted, cip, [this, cip, &path]() { if (!writeIncludePaths(cip->storageDirectory(), cip->customIncludePaths())) { qWarning() << i18n("Failed to save custom include paths in directory: %1", cip->storageDirectory()); } KDevelop::ICore::self()->languageController()->backgroundParser()->addDocument(KDevelop::IndexedString(path)); }); } diff --git a/plugins/custommake/kdevcustommakemanager.json b/plugins/custommake/kdevcustommakemanager.json index 6747812403..cca6433699 100644 --- a/plugins/custommake/kdevcustommakemanager.json +++ b/plugins/custommake/kdevcustommakemanager.json @@ -1,79 +1,75 @@ { "KPlugin": { "Category": "Project Management", "Description": "Imports and edits custom make projects", - "Description[bs]": "Uvozi i uređuje korisnički prilagođene projekte", "Description[ca@valencia]": "Importa i edita projectes personalitzats de Make", "Description[ca]": "Importa i edita projectes personalitzats de Make", "Description[cs]": "Importuje a upravuje vlastní projekty make", "Description[de]": "Import und Bearbeitung benutzerdefinierten Make-Projekten", - "Description[el]": "Εισάγει και επεξεργάζεται προσαρμοσμένα έργα make", "Description[en_GB]": "Imports and edits custom make projects", "Description[es]": "Importa y edita proyectos Make personalizados", - "Description[et]": "Kohandatud make'i projektide importimine ja muutmine", + "Description[et]": "Kohandatud make'i projektide import ja muutmine", "Description[fi]": "Tuo ja muokkaa räätälöityjä make-projekteja", "Description[fr]": "Importe et édite des projets make personnalisés", "Description[gl]": "Importa e edita proxectos que usen un sistema make personalizado.", "Description[it]": "Importa e modifica i progetti personalizzati di Make", "Description[nl]": "Importeert en bewerkt eigen make-projecten", "Description[pl]": "Importuje i edytuje projekty make", "Description[pt]": "Importa e edita projectos do 'make' personalizados", "Description[pt_BR]": "Importa e edita projetos make personalizados", "Description[sk]": "Importuje a upravuje vlastné projekty make", "Description[sl]": "Uvozi in ureja projekte po meri temelječe na Make", "Description[sv]": "Importerar och redigerar egna projekt som använder Make", "Description[tr]": "Özel make projelerini içeriye aktarır ve düzenler", "Description[uk]": "Імпортує і змінює проєкти нетипових make", "Description[x-test]": "xxImports and edits custom make projectsxx", "Description[zh_CN]": "导入并编辑定制 make 工程", "Icon": "kdevelop", "Id": "KDevCustomMakeManager", "Name": "Custom Makefile Project Manager", - "Name[bs]": "Korisnički Makefile menadžer projekta", "Name[ca@valencia]": "Gestor de projecte personalitzat Makefile", "Name[ca]": "Gestor de projecte personalitzat Makefile", "Name[de]": "Projektverwaltung mit eigenem Makefile", - "Name[el]": "Προσαρμοσμένος διαχειριστής έργου makefile", "Name[en_GB]": "Custom Makefile Project Manager", "Name[es]": "Gestor de proyectos Makefile personalizados", "Name[et]": "Kohandatud Makefile'i projektihaldur", "Name[fi]": "Räätälöity Makefile-projektinhallinta", "Name[fr]": "Gestionnaire de projet à Makefile personnalisé", "Name[gl]": "Xestor de proxectos con Makefile personalizado", "Name[it]": "Gestore progetto Makefile personalizzato", "Name[nl]": "Aangepaste projectbeheerder voor makefile", "Name[pl]": "Zarządzanie własnym systemem budowania", - "Name[pt]": "Gestor de Projectos de Makefiles Personalizadas", + "Name[pt]": "Gestor de Projectos com Makefile Personalizado", "Name[pt_BR]": "Gerenciador de Projeto personalizado do Makefile", "Name[sk]": "Správca projektu vlastného makefile", "Name[sl]": "Upravljalnik projektov z datoteko Makefile po meri", "Name[sv]": "Projekthantering av egna Make-filer", "Name[tr]": "Özel Makefile Proje Yöneticisi", "Name[uk]": "Керування проєктами з власним Makefile", "Name[x-test]": "xxCustom Makefile Project Managerxx", "Name[zh_CN]": "自定义 Makefile 工程管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-FileManager": "CustomMake", "X-KDevelop-IRequired": [ "org.kdevelop.IMakeBuilder", "org.kdevelop.IDefinesAndIncludesManager" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBuildSystemManager", "org.kdevelop.IProjectFileManager" ], "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectFilesFilter": [ "GNUmakefile", "Makefile", "makefile", "GNUmakefile.*", "Makefile.*", "makefile.*" ], "X-KDevelop-ProjectFilesFilterDescription": "Makefiles" } diff --git a/plugins/customscript/kdevcustomscript.json b/plugins/customscript/kdevcustomscript.json index 7934a8b28d..83b40620be 100644 --- a/plugins/customscript/kdevcustomscript.json +++ b/plugins/customscript/kdevcustomscript.json @@ -1,94 +1,89 @@ { "KPlugin": { "Authors": [ { "Name": "David Nolden", "Name[ca@valencia]": "David Nolden", "Name[ca]": "David Nolden", "Name[cs]": "David Nolden", "Name[de]": "David Nolden", - "Name[el]": "David Nolden", "Name[en_GB]": "David Nolden", "Name[es]": "David Nolden", "Name[et]": "David Nolden", "Name[fi]": "David Nolden", "Name[fr]": "David Nolden", "Name[gl]": "David Nolden", "Name[it]": "David Nolden", "Name[nl]": "David Nolden", "Name[nn]": "David Nolden", "Name[pl]": "David Nolden", "Name[pt]": "David Nolden", "Name[pt_BR]": "David Nolden", "Name[ru]": "David Nolden", "Name[sk]": "David Nolden", "Name[sl]": "David Nolden", "Name[sv]": "David Nolden", "Name[tr]": "David Nolden", "Name[uk]": "David Nolden", "Name[x-test]": "xxDavid Noldenxx", "Name[zh_CN]": "David Nolden" } ], "Category": "Utilities", "Description": "A plugin for formatting C files using custom scripts", - "Description[bs]": "Dodatak za formatiranje C datoteka koristeći vlastite skripte", "Description[ca@valencia]": "Un connector per a formatar fitxers C emprant scripts personalitzats", "Description[ca]": "Un connector per a formatar fitxers C emprant scripts personalitzats", "Description[de]": "Ein Modul zum Formatieren von C-Dateien mit Hilfe von eigenen Skripten", - "Description[el]": "Ένα πρόσθετο για τη διαμόρφωση C αρχείων με χρήση προσαρμοσμένων σεναρίων", "Description[en_GB]": "A plugin for formatting C files using custom scripts", "Description[es]": "Un complemento para formatear archivos de C usando scripts personalizados", "Description[et]": "Plugin C-failide vormindamiseks kohandatud skriptidega", "Description[fi]": "Liitännäinen C-tiedostojen muotoiluun käyttäen mukautettuja skriptejä", "Description[fr]": "Un module pour mettre en forme des fichiers C d'après des scripts personnalisés", "Description[gl]": "Complemento para formatar os ficheiros en C empregando scripts personalizados.", "Description[it]": "Un'estensione per la formattazione di file in C che usa script personalizzati", "Description[nl]": "Een plugin voor het formatteren van C-bestanden met eigen scripts", "Description[pl]": "Formatuje pliki C przy użyciu własnych skryptów", "Description[pt]": "Um 'plugin' para formatar ficheiros em C com programas personalizados", "Description[pt_BR]": "Um plugin para formatar arquivos em C usando scripts personalizados", "Description[sk]": "Modul pre formátovanie súborov C pomocou vlastných skriptov", "Description[sl]": "Vstavek za oblikovanje izvorne kode C s pomočjo skript po meri", "Description[sv]": "Ett insticksprogram för att formatera C-filer med egna skript", "Description[tr]": "C dosyalarını özel betikler kullanarak biçimlendirmek için bir eklenti", "Description[uk]": "Додаток для форматування файлів мовою C за допомогою нетипових скриптів", "Description[x-test]": "xxA plugin for formatting C files using custom scriptsxx", "Description[zh_CN]": "使用自定义脚本格式化 C 文件的插件", "Icon": "text-field", "Id": "kdevcustomscript", "License": "LGPL", "Name": "Custom Script Formatter Backend", - "Name[bs]": "Pozadinski program za formatiranje vlastitih skripti", "Name[ca@valencia]": "Dorsal de l'script formatador personalitzat", "Name[ca]": "Dorsal de l'script formatador personalitzat", "Name[de]": "Backend für einen Formatierer für eigene Skripte", - "Name[el]": "Προσαρμοσμένο σύστημα υποστήριξης διαμορφωτή σεναρίων", "Name[en_GB]": "Custom Script Formatter Backend", "Name[es]": "Motor de formateo de scripts personalizados", - "Name[et]": "Kohandatud skriptivormindaja taustaprogramm", + "Name[et]": "Kohandatud skriptiga vormindaja taustaprogramm", "Name[fi]": "Mukautetun skriptin muotoilijataustaohjelma", "Name[fr]": "Moteur de mise en forme de script personnalisé", "Name[gl]": "Infraestrutura de formatación de scripts personalizados", "Name[it]": "Backend formattatore script personalizzato", "Name[nl]": "Backend voor formatteerprogramma met eigen scripts", "Name[pl]": "Silnik formatowania przy użyciu własnych skryptów", "Name[pt]": "Infra-Estrutura de Formatação com Programas Personalizados", "Name[pt_BR]": "Infraestrutura de formatação de scripts personalizados", "Name[sk]": "Backend formátovača vlastného skriptu", "Name[sl]": "Zaledje oblikovalnika skript po meri", "Name[sv]": "Formateringsgränssnitt för eget skript", "Name[tr]": "Özel Betik Biçimlendirici Arka Ucu", "Name[uk]": "Сервер скриптів нетипового форматування", "Name[x-test]": "xxCustom Script Formatter Backendxx", "Name[zh_CN]": "自定义脚本格式化器后端", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.ISourceFormatter" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/docker/dockerfile-template/dockerfile-template.desktop b/plugins/docker/dockerfile-template/dockerfile-template.desktop index daed90d927..3a1ed0ce77 100644 --- a/plugins/docker/dockerfile-template/dockerfile-template.desktop +++ b/plugins/docker/dockerfile-template/dockerfile-template.desktop @@ -1,113 +1,107 @@ [General] Name=Dockerfile Name[ca]=Dockerfile Name[ca@valencia]=Dockerfile Name[cs]=Dockerfile Name[de]=Dockerfile -Name[el]=Dockerfile Name[en_GB]=Dockerfile Name[es]=Dockerfile Name[et]=Dockerfile Name[fr]=Fichier Docker Name[gl]=Dockerfile Name[it]=Dockerfile Name[nl]=Dockerfile Name[pl]=Dockerfile Name[pt]=Dockerfile Name[pt_BR]=Dockerfile Name[sk]=Dockerfile -Name[sl]=Dockerfile Name[sv]=Dockerfile Name[tr]=Dockerfile Name[uk]=Dockerfile Name[x-test]=xxDockerfilexx Name[zh_CN]=Dockerfile Comment=A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image Comment[ca]=Un Dockerfile és un document de text que conté totes les ordres que un usuari hauria de cridar des de la línia d'ordres per muntar una imatge Comment[ca@valencia]=Un Dockerfile és un document de text que conté totes les ordres que un usuari hauria de cridar des de la línia d'ordres per muntar una imatge Comment[de]=Eine Docker-Datei ist ein Textdokument, das alle Befehle für das Erstellen des Speicherabbilds eines Containers enthält -Comment[el]=Ένα dockerfile είναι ένα έγγραφο κειμένου που περιέχει όλες τις εντολές που θα μπορούσε να καλέσει ένας χρήστης στη γραμμή εντολών για να συνθέσει μια εικόνα Comment[en_GB]=A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image Comment[es]=Un «Dockerfile» es un documento de texto que contiene todas las órdenes que puede invocar el usuario en la línea de órdenes para ensamblar una imagen Comment[et]=Dockerfile on tekstidokument, mis sisaldab kõiki käske, mida kasutaja saab käsureal pruukida pildi kokkupanemiseks Comment[fr]=Un fichier Docker est un document texte qui contient toutes les commandes qu'un utilisateur pourrait saisir en ligne de commande pour assembler une image Comment[gl]=Un Dockerfile é un documento de texto que contén todas as ordes que un usuario podería chamar desde unha liña de ordes para construír unha imaxe. Comment[it]=Un file Dockerfile è un documento di testo che contiene tutti i comandi che un utente può chiamare da riga di comando per assemblare un'immagine Comment[nl]=Een Dockerbestand is een tekstdocument dat alle opdrachten bevat die een gebruiker zou kunnen aanroepen op de opdrachtregel om een image samen te stellen Comment[pl]=Plik dokowany jest plikiem tekstowym, który zawiera wszystkie polecenia, które użytkownik może wydać w wierszu poleceń, aby złożyć obraz -Comment[pt]=Um Dockerfile é um documento de texto que contém todos os comandos que um utilizador poderá invocar na linha de comandos para montar uma imagem. Um Dockerfile é um documento de texto que contém todos os comandos que um utilizador poderá invocar na linha de comandos para montar uma imagem +Comment[pt]=Um Dockerfile é um documento de texto que contém todos os comandos que um utilizador poderá invocar na linha de comandos para montar uma imagem Comment[pt_BR]=Um Dockerfile é um documento de texto que contém todos os comandos que um usuário poderá invocar na linha de comando para montar uma imagem Comment[sk]=Dockerfile je textový dokument, ktorý obsahuje všetky príkazy, ktoré používať môže volať na príkazovom riadku na zostavenie obrazu -Comment[sl]=Dockerfile je besedilni dokument, ki vsebuje vse ukaze, ki bi jih lahko uporabnik ob izgradnji odtisa izvedel v ukazni vrstici Comment[sv]=En Dockerfile är ett textdokument som innehåller alla kommandon en användare skulle kunna anropa på kommandoraden för att sammanställa en avbild Comment[tr]=Dockerfile, kullanıcının bir imaj oluşturması için komut satırında ayarlayabileceği tüm komutları içiren bir metin belgesidir Comment[uk]=Dockerfile — текстовий документ, який містить усі команди, які користувач може віддавати у командному рядку для збирання образу Comment[x-test]=xxA Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an imagexx Comment[zh_CN]=Dockerfile 是一个包含构建镜像时用户可以在命令行调用的所有命令的文本文档 Category=Docker/Dockerfile Language=JSON Language[ca]=JSON Language[ca@valencia]=JSON Language[cs]=JSON Language[de]=JSON -Language[el]=JSON Language[en_GB]=JSON Language[es]=JSON Language[et]=JSON Language[fr]=JSON Language[gl]=JSON Language[it]=JSON Language[nb]=JSON Language[nl]=JSON Language[nn]=JSON Language[pl]=JSON Language[pt]=JSON Language[pt_BR]=JSON Language[se]=JSON Language[sk]=JSON -Language[sl]=JSON Language[sv]=JSON Language[tr]=JSON Language[uk]=JSON Language[x-test]=xxJSONxx Language[zh_CN]=JSON Files=Implementation OptionsFile=options.kcfg [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=Dockerfile OutputFile=Dockerfile diff --git a/plugins/docker/kdevdocker.json b/plugins/docker/kdevdocker.json index 876854bd7d..3f7d710d66 100644 --- a/plugins/docker/kdevdocker.json +++ b/plugins/docker/kdevdocker.json @@ -1,90 +1,85 @@ { "KPlugin": { "Authors": [ { "Email": "aleixpol@kde.org", "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Runtimes", "Description": "Exposes Docker runtimes", "Description[ca@valencia]": "Exposa el temps d'execució del Docker", "Description[ca]": "Exposa el temps d'execució del Docker", "Description[de]": "Stellt Docker-Laufzeitumgebungen bereit", - "Description[el]": "Εμφανίζει εκτελέσεις του docker", "Description[en_GB]": "Exposes Docker runtimes", "Description[es]": "Expone bibliotecas en tiempo de ejecución de Docker", "Description[et]": "Dockeri käitusaegade näitamine", "Description[fr]": "Expose les exécutifs Docker", "Description[gl]": "Expón os executábeis de Docker.", "Description[it]": "Espone i runtime di Docker", "Description[nl]": "Toont Docker runtimes", "Description[pl]": "Udostępnia biblioteki uruchomieniowe Dockera", - "Description[pt]": "Expõe as bibliotecas do Docker", + "Description[pt]": "Expõe os ambientes de execução do Docker", "Description[pt_BR]": "Expõe as bibliotecas do Docker", "Description[sk]": "Vystavuje Docker runtimes", - "Description[sl]": "Izpostavi izvajalne knjižnice za Docker", "Description[sv]": "Exponerar Docker-körtidsprogram", "Description[tr]": "Docker çalışma zamanlarını gösterir", "Description[uk]": "Надає доступ до середовищ виконання Docker", "Description[x-test]": "xxExposes Docker runtimesxx", "Description[zh_CN]": "暴露 Docker 运行时", "Icon": "kdevelop", "Id": "kdevdocker", "License": "GPL", "Name": "Docker Support", "Name[ca@valencia]": "Implementació del Docker", "Name[ca]": "Implementació del Docker", "Name[cs]": "Podpora Dockeru", "Name[de]": "Docker-Unterstützung", - "Name[el]": "Υποστήριξη docker", "Name[en_GB]": "Docker Support", "Name[es]": "Implementación de Docker", - "Name[et]": "Dockeri toetus", "Name[fr]": "Prise en charge de Docker", "Name[gl]": "Integración de Docker", "Name[it]": "Supporto per Docker", "Name[nl]": "Ondersteuning van Docker", "Name[pl]": "Obsługa Docker", "Name[pt]": "Suporte para o Docker", "Name[pt_BR]": "Suporte ao Docker", "Name[sk]": "Podpora Docker", "Name[sl]": "Podpora za Docker", "Name[sv]": "Docker-stöd", "Name[tr]": "Docker Desteği", "Name[uk]": "Підтримка Docker", "Name[x-test]": "xxDocker Supportxx", "Name[zh_CN]": "Docker 支持", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/documentswitcher/kdevdocumentswitcher.json b/plugins/documentswitcher/kdevdocumentswitcher.json index 9ae080e62d..7cacf915f2 100644 --- a/plugins/documentswitcher/kdevdocumentswitcher.json +++ b/plugins/documentswitcher/kdevdocumentswitcher.json @@ -1,100 +1,88 @@ { "KPlugin": { "Authors": [ { "Name": "Andreas Pakulat", "Name[ca@valencia]": "Andreas Pakulat", "Name[ca]": "Andreas Pakulat", "Name[cs]": "Andreas Pakulat", "Name[de]": "Andreas Pakulat", - "Name[el]": "Andreas Pakulat", "Name[en_GB]": "Andreas Pakulat", "Name[es]": "Andreas Pakulat", "Name[et]": "Andreas Pakulat", "Name[fr]": "Andreas Pakulat", "Name[gl]": "Andreas Pakulat", "Name[it]": "Andreas Pakulat", "Name[nl]": "Andreas Pakulat", "Name[nn]": "Andreas Pakulat", "Name[pl]": "Andreas Pakulat", "Name[pt]": "Andreas Pakulat", "Name[pt_BR]": "Andreas Pakulat", "Name[ru]": "Andreas Pakulat", "Name[sk]": "Andreas Pakulat", "Name[sl]": "Andreas Pakulat", "Name[sv]": "Andreas Pakulat", "Name[tr]": "Andreas Pakulat", "Name[uk]": "Andreas Pakulat", "Name[x-test]": "xxAndreas Pakulatxx", "Name[zh_CN]": "Andreas Pakulat" } ], "Category": "Utilities", "Description": "A most-recently-used document switcher for KDevPlatform.", "Description[ca@valencia]": "Un commutador del document usat més recentment per KDevPlatform.", "Description[ca]": "Un commutador del document usat més recentment per KDevPlatform.", "Description[cs]": "Přepínač nedávno použitých dokumentů pro KDevPlatform", "Description[de]": "Ein Umschalter zwischen zuletzt geöffneten Dokumenten für KDevPlatform.", - "Description[el]": "Ένας εναλλάκτης μεταξύ πρόσφατα χρησιμοποιημένων εγγράφων για το KDevPlatform.", "Description[en_GB]": "A most-recently-used document switcher for KDevPlatform.", "Description[es]": "Un cambiador de documentos recientemente usados para KDevPlatform.", "Description[et]": "KDevPlatformi viimati kasutatud dokumentide vahetaja", "Description[fr]": "Un changeur de document dernièrement utilisé pour KDevPlatform.", "Description[gl]": "Un selector entre documentos empregados recentemente para KDevPlatform.", "Description[it]": "Uno scambia documento utilizzato più di recente per KDevPlatform.", "Description[nl]": "De meest-recent-gebruikte documentwisselaar voor KDevPlatform.", "Description[pl]": "Przechodzi pomiędzy ostatnio używanymi dokumentami.", "Description[pt]": "Um selector dos documentos usados mais recentemente para o KDevPlatform.", "Description[pt_BR]": "Um seletor dos documentos mais recentes para o KDevPlatform.", "Description[sk]": "Prepínač posledných použitých dokumentov pre KDevPlatform.", "Description[sl]": "Vstavek za preklapljanje med nazadnje uporabljenimi dokumenti.", "Description[sv]": "Byte till senast använda dokument för KDevelop-plattformen.", "Description[tr]": "KDevPlatform için en-son-kullanılan belge seçici bir eklenti.", "Description[uk]": "Перемикач останніх використаних документів для KDevPlatform.", "Description[x-test]": "xxA most-recently-used document switcher for KDevPlatform.xx", "Description[zh_CN]": "KDevPlatform 的最近经常使用文档的切换器。", - "Description[zh_TW]": "KDevPlatform 上切換最近使用的文件的切換器", "Icon": "document-open-recent", "Id": "kdevdocumentswitcher", "License": "GPL", "Name": "Most-Recently-Used Document Switcher", - "Name[ar]": "مبدّل المستندات المستخدمة-حديثًا", - "Name[bg]": "Последният използван превключвател на документи", - "Name[bs]": "Mjenjač zadnjeg korištenog dokumenta", "Name[ca@valencia]": "Commutador del document usat més recentment", "Name[ca]": "Commutador del document usat més recentment", "Name[cs]": "Přepínač nedávno používaných dokumentů ", - "Name[da]": "Dokumentskifter til nyligt anvendte", "Name[de]": "Zuletzt-Verwendet-Dokumentumschalter", - "Name[el]": "Εναλλάκτης μεταξύ πρόσφατα χρησιμοποιημένων εγγράφων", "Name[en_GB]": "Most-Recently-Used Document Switcher", "Name[es]": "Selector de documentos usados recientemente", "Name[et]": "Viimati kasutatud dokumentide vahetaja", "Name[fr]": "Commutateur de documents parmi les plus récemment utilisés", "Name[gl]": "Selector entre documentos empregados recentemente", - "Name[hu]": "Dokumentumváltó", "Name[it]": "Scambia documento utilizzato più di recente", - "Name[kk]": "Жуырда қолданған құжаттар ауыстырғышы", "Name[nb]": "Sist-brukte dokumentbytter", - "Name[nds]": "Togrieper för de tolest bruukten Dokmenten", "Name[nl]": "Meest-recent-gebruikte documentwisselaar", "Name[pl]": "Przełączanie między ostatnio używanymi dokumentami", "Name[pt]": "Selector dos Documentos Usados Mais Recentemente", "Name[pt_BR]": "Seletor dos documentos usados mais recentemente", "Name[ru]": "Переключатель недавних документов", "Name[sk]": "Prepínač posledných použitých dokumentov", "Name[sl]": "Preklapljanje med nazadnje uporabljenimi dokumenti", "Name[sv]": "Byte till senast använda dokument", "Name[tr]": "Yakın Zamanda En Çok Kullanılan Belgeleri Seçici", "Name[uk]": "Перемикач нещодавно використаних документів", "Name[x-test]": "xxMost-Recently-Used Document Switcherxx", "Name[zh_CN]": "最近经常使用文档切换器", - "Name[zh_TW]": "最近使用的文件切換器", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/documentview/kdevdocumentview.json b/plugins/documentview/kdevdocumentview.json index 4d46287468..34e61a56f0 100644 --- a/plugins/documentview/kdevdocumentview.json +++ b/plugins/documentview/kdevdocumentview.json @@ -1,100 +1,87 @@ { "KPlugin": { "Authors": [ { "Name": "Adam Treat", "Name[ca@valencia]": "Adam Treat", "Name[ca]": "Adam Treat", "Name[cs]": "Adam Treat", "Name[de]": "Adam Treat", - "Name[el]": "Adam Treat", "Name[en_GB]": "Adam Treat", "Name[es]": "Adam Treat", "Name[et]": "Adam Treat", "Name[fr]": "Adam Treat", "Name[gl]": "Adam Treat", "Name[it]": "Adam Treat", "Name[nl]": "Adam Treat", "Name[nn]": "Adam Treat", "Name[pl]": "Adam Treat", "Name[pt]": "Adam Treat", "Name[pt_BR]": "Adam Treat", "Name[ru]": "Adam Treat", "Name[sk]": "Adam Treat", "Name[sl]": "Adam Treat", "Name[sv]": "Adam Treat", "Name[tr]": "Adam Treat", "Name[uk]": "Adam Treat", "Name[x-test]": "xxAdam Treatxx", "Name[zh_CN]": "Adam Treat" } ], "Category": "Utilities", "Description": "This plugin displays a graphical view of all documents currently loaded and separates them by mimetype.", "Description[ca@valencia]": "Aquest connector mostra una visualització gràfica de tots els documents carregats actualment, separats per tipus MIME.", "Description[ca]": "Aquest connector mostra una visualització gràfica de tots els documents carregats actualment, separats per tipus MIME.", "Description[cs]": "Tento zásuvný modul zobrazuje grafický pohled všech aktuálně načtených dokumentů a odděluje je podle typu MIME.", "Description[de]": "Dieses Modul zeigt alle aktuell geladenen Dokumente getrennt nach ihren MIME-Typen an.", - "Description[el]": "Το πρόσθετο αυτό εμφανίζει μια γραφική προβολή όλων των εγγράφων που φορτώνονται και τα διαχωρίζει με βάση το αναγνωριστικό τύπου.", "Description[en_GB]": "This plugin displays a graphical view of all documents currently loaded and separates them by mimetype.", "Description[es]": "Este complemento muestra una vista gráfica de todos los documentos actualmente cargados y los separa por su tipo MIME.", "Description[et]": "See plugin näitab graafiliselt kõiki laaditud dokumente ja eraldab need MIME tüübi alusel.", "Description[fr]": "Ce module affiche une vue graphique de tous les documents actuellement chargés et les sépare par type MIME.", "Description[gl]": "Este complemento mostra unha vista gráfica de todos os documentos que están cargados e sepáraos segundo o seu tipo mime.", "Description[it]": "Questa estensione mostra una vista grafica di tutti i documenti attualmente caricati e li separa per il tipo MIME.", "Description[nl]": "Deze plugin toont een grafische voorstelling van alle nu geladen documenten en scheidt deze door het mimetype.", "Description[pl]": "Umożliwia przeglądanie otwartych dokumentów w widoku graficznym i dzieli je po typach mime.", "Description[pt]": "Este 'plugin' mostra uma vista gráfica sobre todos os documentos abertos de momento e separa-os pelo seu tipo MIME.", "Description[pt_BR]": "Este plugin mostra uma vista gráfica sobre todos os documentos carregados no momento e separa-os por tipo MIME.", "Description[sk]": "Tento plugin zobrazí grafický pohľad všetkých dokumentov aktuálne načítaných a rozdelí ich podľa mimetype.", "Description[sl]": "Ta vstavek prikazuje vse trenutno naložene dokumente in jih ločuje glede na vrsto MIME.", "Description[sv]": "Insticksprogrammet visar en grafisk vy av alla dokument som för närvarande har laddats och delar upp dem enligt Mime-typ.", "Description[tr]": "Bu eklenti o anda yüklenmiş olan tüm belgeleri grafiksel bir görünümde gösterir ve mime türlerine göre ayırır.", "Description[uk]": "Цей додаток відображає у графічному вигляді всі відкриті документи і впорядковує їх за типом MIME.", "Description[x-test]": "xxThis plugin displays a graphical view of all documents currently loaded and separates them by mimetype.xx", "Description[zh_CN]": "此插件显示当前已装入文档的图形视图并按照 mime 类型分类。", - "Description[zh_TW]": "這個外掛程式顯示目前載入並依 MIME 型態分類的文件的圖形檢視。", "Icon": "document-preview", "Id": "kdevdocumentview", "License": "LGPL", "Name": "Document View", - "Name[bg]": "Изглед за документи", "Name[ca@valencia]": "Visor de document", "Name[ca]": "Visor de document", "Name[cs]": "Pohled na dokumenty", - "Name[da]": "Dokumentvisning", "Name[de]": "Dokumentansicht", - "Name[el]": "Προβολή εγγράφου", "Name[en_GB]": "Document View", "Name[es]": "Vista de documento", - "Name[et]": "Dokumendivaade", "Name[fr]": "Vue Document", - "Name[ga]": "Amharc Cáipéise", "Name[gl]": "Vista do documento", - "Name[hu]": "Dokumentumnézet", "Name[it]": "Vista documento", - "Name[kk]": "Құжат көрінісі", "Name[nb]": "Dokumentvisning", - "Name[nds]": "Dokmentenansicht", "Name[nl]": "Documentweergave", "Name[pl]": "Widok dokumentu", "Name[pt]": "Área de Documentos", "Name[pt_BR]": "Visualização de documentos", "Name[ru]": "Панель документов", "Name[sk]": "Pohľad na dokumenty", "Name[sl]": "Prikaz dokumentov", "Name[sv]": "Dokumentvisning", "Name[tr]": "Belge Görünümü", - "Name[ug]": "پۈتۈك كۆرۈنۈشى", "Name[uk]": "Перегляд документів", "Name[x-test]": "xxDocument Viewxx", "Name[zh_CN]": "文档视图", - "Name[zh_TW]": "文件檢視", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/documentview/settings/kcm_documentview_settings.desktop b/plugins/documentview/settings/kcm_documentview_settings.desktop index fe8351d0de..7fbb1068f2 100644 --- a/plugins/documentview/settings/kcm_documentview_settings.desktop +++ b/plugins/documentview/settings/kcm_documentview_settings.desktop @@ -1,78 +1,77 @@ [Desktop Entry] Icon=kdevelop Type=Service ServiceTypes=KCModule X-KDE-ModuleType=Library X-KDE-Library=kcm_documentview_settings X-KDE-FactoryName=kcm_documentview_settings X-KDE-ParentApp=kdevelop X-KDE-ParentComponents=kdevelop X-KDE-CfgDlgHierarchy=CORE Name=Document View Name[bg]=Изглед за документи Name[bs]=Pregled Dokumenta Name[ca]=Visor de document Name[ca@valencia]=Visor de document Name[cs]=Pohled na dokumenty Name[de]=Dokumentansicht -Name[el]=Προβολή εγγράφου Name[en_GB]=Document View Name[es]=Vista de documento Name[et]=Dokumendivaade Name[fr]=Vue Document Name[ga]=Amharc Cáipéise Name[gl]=Vista do documento Name[it]=Vista documento Name[kk]=Құжат көрінісі Name[nb]=Dokumentvisning Name[nds]=Dokmenten-Ansicht Name[nl]=Documentweergave Name[pl]=Widok dokumentu Name[pt]=Área de Documentos Name[pt_BR]=Área de documentos Name[ru]=Панель документов Name[sk]=Pohľad na dokumenty Name[sl]=Prikaz dokumentov Name[sv]=Dokumentvisning Name[tr]=Belge Görünümü Name[ug]=پۈتۈك كۆرۈنۈشى Name[uk]=Перегляд документів Name[x-test]=xxDocument Viewxx Name[zh_CN]=文档视图 Name[zh_TW]=文件檢視 Comment=Configure Document View settings Comment[bg]=Настройки на изгледа за документи Comment[ca]=Configura els arranjaments del visor de document Comment[ca@valencia]=Configura els arranjaments del visor de document Comment[cs]=Upravit nastavení zobrazení dokumentu Comment[de]=Einstellungen für Dokumentansicht -Comment[el]=Διαμόρφωση ρυθμίσεων προβολής εγγράφου +Comment[el]=Ρύθμιση επιλογών προβολής εγγράφου Comment[en_GB]=Configure Document View settings Comment[es]=Configurar las preferencias de la vista de documento Comment[et]=Dokumendivaate valikute seadistamine Comment[fr]=Configurer les paramètres de la vue Document Comment[ga]=Cumraigh an tAmharc Cáipéise Comment[gl]=Configura a vista de documento Comment[hi]=दस्तावेज़ दृश्य विन्यास कॉन्फ़िगर करें Comment[hne]=कागद दृस्य सेटिंग कान्फिगर करव Comment[it]=Configura impostazioni di visualizzazione documento Comment[ja]=文書ビューのオプションを設定します Comment[lv]=Konfigurē dokumentu skata iestatījumus Comment[nb]=Sett opp innstillinger for dokumentvisning Comment[nds]=Dokmenten-Ansicht instellen Comment[ne]=कागजात दृश्य सेटिङ कन्फिगर गर्नुहोस् Comment[nl]=Documentweergave-instellingen configureren Comment[pl]=Ustawienia widoku dokumentu Comment[pt]=Configurar as opções da Área de Documentos Comment[pt_BR]=Configurar as opções da área de documentos Comment[sk]=Nastaviť možnosti zobrazenia dokumentov Comment[sl]=Nastavite možnosti za Prikaz dokumentov Comment[sv]=Anpassa inställningar av dokumentvisning Comment[tr]=Belge Görünümü Ayarlarını Yapılandır Comment[uk]=Налаштувати параметри перегляду документа Comment[x-test]=xxConfigure Document View settingsxx Comment[zh_CN]=配置文档视图设置 Comment[zh_TW]=設定文件檢視 diff --git a/plugins/execute/kdevexecute.json b/plugins/execute/kdevexecute.json index 293dd4f446..ecde46f47f 100644 --- a/plugins/execute/kdevexecute.json +++ b/plugins/execute/kdevexecute.json @@ -1,104 +1,90 @@ { "KPlugin": { "Authors": [ { "Name": "Hamish Rodda", "Name[ca@valencia]": "Hamish Rodda", "Name[ca]": "Hamish Rodda", "Name[cs]": "Hamish Rodda", "Name[de]": "Hamish Rodda", - "Name[el]": "Hamish Rodda", "Name[en_GB]": "Hamish Rodda", "Name[es]": "Hamish Rodda", "Name[et]": "Hamish Rodda", "Name[fr]": "Hamish Rodda", "Name[gl]": "Hamish Rodda", "Name[it]": "Hamish Rodda", "Name[nl]": "Hamish Rodda", "Name[nn]": "Hamish Rodda", "Name[pl]": "Hamish Rodda", "Name[pt]": "Hamish Rodda", "Name[pt_BR]": "Hamish Rodda", "Name[ru]": "Hamish Rodda", "Name[sk]": "Hamish Rodda", "Name[sl]": "Hamish Rodda", "Name[sv]": "Hamish Rodda", "Name[tr]": "Hamish Rodda", "Name[uk]": "Hamish Rodda", "Name[x-test]": "xxHamish Roddaxx", "Name[zh_CN]": "Hamish Rodda" } ], "Category": "Core", "Description": "This plugin allows running of programs with no instrumentor, ie. natively by the current host.", "Description[ca@valencia]": "Aquest connector permet executar programes sense «instrumentor», és a dir, nativament per a la màquina actual.", "Description[ca]": "Aquest connector permet executar programes sense «instrumentor», és a dir, nativament per a la màquina actual.", "Description[cs]": "Tento zásuvný modul umožňuje běh programů bez jakéhokoliv prostředníka, takže běží přirozeně na současném hostiteli.", "Description[de]": "Dieses Modul erlaubt das Ausführen von Programmen im Kontext des Betriebssystems.", - "Description[el]": "Αυτό το πρόσθετο επιτρέπει την εκτέλεση προγραμμάτων χωρίς οργανωτή, πχ. εγγενώς από τον τρέχων υπολογιστή.", "Description[en_GB]": "This plugin allows running of programs with no instrumentor, ie. natively by the current host.", "Description[es]": "Este complemento permite ejecutar programas sin instrumentador (es decir, de forma nativa) en la máquina actual.", "Description[et]": "See plugin võimaldab panna programme aktiivses masinas tööle ilma instrumentaatorita, s.t loomulikult.", "Description[fr]": "Ce module permet d'exécuter des programmes sans instrumentation, c'est-à-dire de manière native sur l'hôte courant.", "Description[gl]": "Este complemento permite executar programas sen instrumentador, i.e. de xeito nativo na máquina actual.", "Description[it]": "Questa estensione permette l'esecuzione dei programmi senza instrumentor, vale a dire nativamente da parte dell'host attuale.", "Description[nl]": "Deze plugin staat het uitvoeren van programma's toe zonder hulpprogramma, dwz. van nature op de huidige host.", "Description[pl]": "Uruchamia program bez instrumentora, np. macierzyście przez bieżącego gospodarza.", "Description[pt]": "Este 'plugin' permite a execução de programas sem instrumentação, i.e. nativamente na máquina-anfitriã actual.", "Description[pt_BR]": "Este plugin permite executar programas sem orquestrador, ou seja, nativamente pelo computador hospedeiro.", "Description[sk]": "Tento plugin umožňuje spustenie programov bez inštrumentora, teda natívne aktuálnym hostiteľom.", "Description[sl]": "Vstavek omogoča zaganjanje programov, za katere v KDevelop ni posebnega grafičnega vmesnika.", "Description[sv]": "Insticksprogrammet tillåter att program utan instrumentering körs, dvs. direkt av nuvarande värddator.", "Description[tr]": "Bu eklenti programların ek araç olmadan çalıştırılabilmesini sağlar, örn. mevcut istemci ile doğal olarak.", "Description[uk]": "За допомогою цього додатка можна запускати програми безпосередньо на поточному вузлі.", "Description[x-test]": "xxThis plugin allows running of programs with no instrumentor, ie. natively by the current host.xx", "Description[zh_CN]": "此插件允许以无操作者方式运行程序,例如,当前主机的原生方式。", - "Description[zh_TW]": "此外掛程式允許在目前主機上執行程式。", "Icon": "system-run", "Id": "kdevexecute", "License": "GPL", "Name": "Execute Programs", - "Name[ar]": "نفّذ برامجًا", - "Name[bg]": "Изпълняване на програми", - "Name[bs]": "Izvrši programe", "Name[ca@valencia]": "Executa programes", "Name[ca]": "Executa programes", "Name[cs]": "Spustit programy", - "Name[da]": "Kør programmer", "Name[de]": "Programme ausführen", - "Name[el]": "Εκτέλεση προγραμμάτων", "Name[en_GB]": "Execute Programs", "Name[es]": "Ejecutar programas", "Name[et]": "Programmide täitmine", "Name[fr]": "Exécuter des programmes", - "Name[ga]": "Rith Ríomhchláir", "Name[gl]": "Executar programas", - "Name[hu]": "Programok végrehajtása", "Name[it]": "Esegui i programmi", - "Name[kk]": "Бағдарламаларды орындау", "Name[nb]": "Kjør programmer", - "Name[nds]": "Programmen utföhren", "Name[nl]": "Programma's uitvoeren", "Name[pl]": "Wykonaj programy", "Name[pt]": "Execução de Programas", - "Name[pt_BR]": "Executar programa", + "Name[pt_BR]": "Executar programas", "Name[ru]": "Запуск программ", "Name[sk]": "Spustiť programy", "Name[sl]": "Izvedi programe", "Name[sv]": "Kör program", "Name[tr]": "Uygulamaları Çalıştır", - "Name[ug]": "پروگرامما ئىجرا قىلىش", "Name[uk]": "Виконання програм", "Name[x-test]": "xxExecute Programsxx", "Name[zh_CN]": "执行程序", - "Name[zh_TW]": "執行程式", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IExecutePlugin" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/executeplasmoid/kdevexecuteplasmoid.json b/plugins/executeplasmoid/kdevexecuteplasmoid.json index 6415fabceb..ab9b5c4900 100644 --- a/plugins/executeplasmoid/kdevexecuteplasmoid.json +++ b/plugins/executeplasmoid/kdevexecuteplasmoid.json @@ -1,95 +1,89 @@ { "KPlugin": { "Authors": [ { "Name": "Aleix Pol Gonzalez", "Name[ca@valencia]": "Aleix Pol Gonzalez", "Name[ca]": "Aleix Pol Gonzalez", "Name[cs]": "Aleix Pol Gonzalez", "Name[de]": "Aleix Pol Gonzalez", - "Name[el]": "Aleix Pol Gonzalez", "Name[en_GB]": "Aleix Pol Gonzalez", "Name[es]": "Aleix Pol Gonzalez", "Name[et]": "Aleix Pol Gonzalez", "Name[fi]": "Aleix Pol Gonzalez", "Name[fr]": "Aleix Pol Gonzalez", "Name[gl]": "Aleix Pol Gonzalez", "Name[it]": "Aleix Pol Gonzalez", "Name[nl]": "Aleix Pol Gonzalez", "Name[nn]": "Aleix Pol Gonzalez", "Name[pl]": "Aleix Pol Gonzalez", "Name[pt]": "Aleix Pol Gonzalez", "Name[pt_BR]": "Aleix Pol Gonzalez", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol Gonzalez", "Name[sl]": "Aleix Pol Gonzalez", "Name[sv]": "Aleix Pol Gonzalez", "Name[tr]": "Aleix Pol Gonzalez", "Name[uk]": "Aleix Pol Gonzalez", "Name[x-test]": "xxAleix Pol Gonzalezxx", "Name[zh_CN]": "Aleix Pol Gonzalez" } ], "Description": "Lets you view a plasmoid", - "Description[bs]": "Omogućava da vidite plazmoid", "Description[ca@valencia]": "Vos permet veure un plasmoide", "Description[ca]": "Us permet veure un plasmoide", "Description[de]": "Zeigt ein Plasmoid an", - "Description[el]": "Σας επιτρέπει να προβάλετε ένα πλασμοειδές", "Description[en_GB]": "Lets you view a plasmoid", "Description[es]": "Le permite ver un plasmoide", "Description[et]": "Võimaldab näha plasmoidi", "Description[fi]": "Mahdollistaa plasmoidin katselun", "Description[fr]": "Vous permet d'afficher un plasmoïde", "Description[gl]": "Permite ver un plasmoide.", "Description[it]": "Consente di visualizzare un plasmoide", "Description[nl]": "Laat u een plasmoid bekijken", "Description[pl]": "Pokazuje plazmoid", "Description[pt]": "Permite-lhe visualizar um plasmóide", "Description[pt_BR]": "Permite-lhe visualizar um plasmoide", "Description[sk]": "Umožní vám vidieť plazmoid", "Description[sl]": "Vam omogoča ogled plasmoida", "Description[sv]": "Låter dig visa en Plasmoid", "Description[tr]": "Bir plazmoid görüntülemenize izin verir", "Description[uk]": "Надає вам змогу переглядати роботу плазмоїдів", "Description[x-test]": "xxLets you view a plasmoidxx", "Description[zh_CN]": "让您查看一个 plasmoid", "Icon": "system-run", "Id": "kdevexecuteplasmoid", "License": "GPL", "Name": "Plasmoid Launcher", - "Name[bs]": "Pokretač plazmoida", "Name[ca@valencia]": "Llançador de plasmoide", "Name[ca]": "Llançador de plasmoide", "Name[cs]": "Spouštěč Plasmoidů", "Name[de]": "Plasmoid-Starter", - "Name[el]": "Εκτελεστής πλασμοειδών", "Name[en_GB]": "Plasmoid Launcher", "Name[es]": "Lanzador de plasmoides", - "Name[et]": "Plasmoidide käivitaja", + "Name[et]": "Plasmoidi käivitaja", "Name[fi]": "Plasmoidikäynnistin", "Name[fr]": "Lanceur Plasmoïde", "Name[gl]": "Iniciador de plasmoides", - "Name[hu]": "Plasmoid indító", "Name[it]": "Lanciatore di plasmoidi", "Name[nl]": "Programmastarter van plasmoid", "Name[pl]": "Program uruchamiający plazmoid", "Name[pt]": "Lançamento de Plasmóides", "Name[pt_BR]": "Lançador de plasmoides", "Name[sk]": "Spúšťač plazmoidov", "Name[sl]": "Zaganjalnik plasmoidov", "Name[sv]": " Plasmoid-startprogram", "Name[tr]": "Plazmoid Başlatıcısı", "Name[uk]": "Засіб запуску плазмоїдів", "Name[x-test]": "xxPlasmoid Launcherxx", "Name[zh_CN]": "部件启动器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IExecutePlugin" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/executescript/kdevexecutescript.json b/plugins/executescript/kdevexecutescript.json index 1fc9571b58..a2d4fbd290 100644 --- a/plugins/executescript/kdevexecutescript.json +++ b/plugins/executescript/kdevexecutescript.json @@ -1,103 +1,89 @@ { "KPlugin": { "Authors": [ { "Name": "Niko Sams", "Name[ca@valencia]": "Niko Sams", "Name[ca]": "Niko Sams", "Name[cs]": "Niko Sams", "Name[de]": "Niko Sams", - "Name[el]": "Niko Sams", "Name[en_GB]": "Niko Sams", "Name[es]": "Niko Sams", "Name[et]": "Niko Sams", "Name[fr]": "Niko Sams", "Name[gl]": "Niko Sams", "Name[it]": "Niko Sams", "Name[nl]": "Niko Sams", "Name[nn]": "Niko Sams", "Name[pl]": "Niko Sams", "Name[pt]": "Niko Sams", "Name[pt_BR]": "Niko Sams", "Name[ru]": "Niko Sams", "Name[sk]": "Niko Sams", "Name[sl]": "Niko Sams", "Name[sv]": "Niko Sams", "Name[tr]": "Niko Sams", "Name[uk]": "Niko Sams", "Name[x-test]": "xxNiko Samsxx", "Name[zh_CN]": "Niko Sams" } ], "Description": "This plugin allows running of scripts.", - "Description[ar]": "تسمح هذه الملحقة بتنفيذ السّكرِبتات.", "Description[ca@valencia]": "Aquest connector permet executar scripts.", "Description[ca]": "Aquest connector permet executar scripts.", "Description[cs]": "Tento modul umožňuje spouštění skriptů.", "Description[de]": "Dieses Modul ermöglicht das Ausführen von Skripten.", - "Description[el]": "Το πρόσθετο αυτό επιτρέπει την εκτέλεση σεναρίων.", "Description[en_GB]": "This plugin allows running of scripts.", "Description[es]": "Este complemento permite la ejecución de scripts.", "Description[et]": "See plugin võimaldab käivitada skripte.", "Description[fr]": "Ce module permet d'exécuter des scripts.", "Description[gl]": "Este complemento permite a execución de scripts.", "Description[it]": "Questa estensione permette l'esecuzione degli script.", "Description[nl]": "Deze plugin staat het uitvoeren van scripts toe.", "Description[pl]": "Umożliwia uruchamianie skryptów.", "Description[pt]": "Este 'plugin' permite a execução de programas.", - "Description[pt_BR]": "Este plugin permite a execução de programas.", + "Description[pt_BR]": "Este plugin permite a execução de scripts.", "Description[sk]": "Tento plugin povoľuje spúšťanie skriptov.", "Description[sl]": "Ta vstavek omogoča zaganjanje skript.", "Description[sv]": "Insticksprogrammet gör det möjligt att köra skript.", "Description[tr]": "Bu eklenti betiklerin çalıştırılmasını sağlar.", "Description[uk]": "За допомогою цього додатка можна запускати скрипти.", "Description[x-test]": "xxThis plugin allows running of scripts.xx", "Description[zh_CN]": "此插件可以执行脚本.", - "Description[zh_TW]": "此外掛程式允許執行文稿。", "Icon": "system-run", "Id": "kdevexecutescript", "License": "GPL", "Name": "Execute Scripts", - "Name[ar]": "نفّذ سكرِبتات", - "Name[bg]": "Изпълнение на скриптове", - "Name[bs]": "Izvrši skruptu", "Name[ca@valencia]": "Executa scripts", "Name[ca]": "Executa scripts", "Name[cs]": "Spustit skripty", - "Name[da]": "Kør scripts", "Name[de]": "Skripte ausführen", - "Name[el]": "Εκτέλεση σεναρίων", "Name[en_GB]": "Execute Scripts", "Name[es]": "Ejecutar scripts", "Name[et]": "Skriptide käivitamine", "Name[fr]": "Exécution de scripts", - "Name[ga]": "Rith Scripteanna", "Name[gl]": "Executar scripts", - "Name[hu]": "Parancsfájlok végrehajtása", "Name[it]": "Esegui script", - "Name[kk]": "Скрипттерді орындқу", "Name[nb]": "Kjør skripter", - "Name[nds]": "Skripten utföhren", "Name[nl]": "Scripts uitvoeren", "Name[pl]": "Wykonywanie skryptów", "Name[pt]": "Executar Programas", - "Name[pt_BR]": "Executar os programas", + "Name[pt_BR]": "Executar scripts", "Name[ru]": "Запуск сценариев", "Name[sk]": "Spustiť skripty", "Name[sl]": "Izvedi skripte", "Name[sv]": "Kör skript", "Name[tr]": "Betikleri Çalıştır", "Name[uk]": "Виконання скриптів", "Name[x-test]": "xxExecute Scriptsxx", "Name[zh_CN]": "执行脚本", - "Name[zh_TW]": "執行文稿", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IExecuteScriptPlugin" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/externalscript/kdevexternalscript.json b/plugins/externalscript/kdevexternalscript.json index f3fb4d41a7..cb8c93eac9 100644 --- a/plugins/externalscript/kdevexternalscript.json +++ b/plugins/externalscript/kdevexternalscript.json @@ -1,107 +1,93 @@ { "KPlugin": { "Authors": [ { "Name": "Milian Wolff", "Name[ca@valencia]": "Milian Wolff", "Name[ca]": "Milian Wolff", "Name[cs]": "Milian Wolff", "Name[de]": "Milian Wolff", - "Name[el]": "Milian Wolff", "Name[en_GB]": "Milian Wolff", "Name[es]": "Milian Wolff", "Name[et]": "Milian Wolff", "Name[fr]": "Milian Wolff", "Name[gl]": "Milian Wolff", "Name[it]": "Milian Wolff", "Name[nl]": "Milian Wolff", "Name[nn]": "Milian Wolff", "Name[pl]": "Milian Wolff", "Name[pt]": "Milian Wolff", "Name[pt_BR]": "Milian Wolff", "Name[ru]": "Milian Wolff", "Name[sk]": "Milian Wolff", "Name[sl]": "Milian Wolff", "Name[sv]": "Milian Wolff", "Name[tr]": "Milian Wolff", "Name[uk]": "Milian Wolff", "Name[x-test]": "xxMilian Wolffxx", "Name[zh_CN]": "Milian Wolff" } ], "Category": "Utilities", "Description": "Run external scripts or applications to manipulate the editor contents or do other arbitrary actions.", - "Description[ar]": "شغّل سكرِبتات أو تطبيقات خارجيّة للتّلاعب بمحتويات المحرّر أو عمل إجراءات اعتباطيّة أخرى.", "Description[ca@valencia]": "Executa scripts externs o aplicacions per a manipular el contingut de l'editor o altres accions arbitràries.", "Description[ca]": "Executa scripts externs o aplicacions per a manipular el contingut de l'editor o altres accions arbitràries.", "Description[cs]": "Spouštějte externí skripty nebo aplikace a pracujte s obsahem editoru nebo dělejte jiné libovolné akce.", "Description[de]": "Führen Sie externe Skripte oder Programme zum Verändern des Editorinhalts oder für beliebige andere Aktionen aus.", - "Description[el]": "Εκτέλεση εξωτερικών σεναρίων ή εφαρμογών για χειρισμό του περιεχομένου \"\"του κειμενογράφου ή για οποιεσδήποτε άλλες ενέργειες.", "Description[en_GB]": "Run external scripts or applications to manipulate the editor contents or do other arbitrary actions.", "Description[es]": "Ejecutar scripts externos o aplicaciones para manipular el contenido del editor o realizar otras acciones.", "Description[et]": "Välised skriptid või rakendused, mis võimaldavad muuta redaktori sisu või ette võtta mingeid muid toiminguid.", "Description[fr]": "Exécuter des scripts externes ou des applications pour manipuler les contenus de l'éditeur ou autres actions arbitraires.", "Description[gl]": "Executa scripts externos ou aplicacións para manipular os contidos do editor ou levar a cabo outras accións.", "Description[it]": "Avvia script o applicazioni esterne per manipolare il contenuto dell'editor o per eseguire altre azioni.", "Description[nl]": "Externe scripts of programma's uitvoeren om de inhoud van de bewerker te manipuleren of andere acties uit te voeren.", "Description[pl]": "Uruchamia zewnętrzne skrypty lub programy celem zmiany treści edytora lub wykonania innych działań.", "Description[pt]": "Executa programas ou aplicações externas para manipular o conteúdo do editor ou para efectuar outras acções arbitrárias.", "Description[pt_BR]": "Execute scripts externos ou aplicativos para manipular os conteúdos do editor ou para fazer outras ações ordinárias.", "Description[sk]": "Spustí externé skripty alebo aplikácie na manipuláciu s obsahom editora alebo robí iné ľubovoľné akcie.", "Description[sl]": "Zaganjajte zunanje skripte ali programe, ki upravljajo z vsebino urejevalnika ali pa opravljajo druga poljubna dejanja.", "Description[sv]": "Kör externa skript eller program för att behandla editorns innehåll eller utför andra godtyckliga åtgärder.", "Description[tr]": "Düzenleyici içeriğini değiştirmek veya diğer keyfi eylemler için dış betikler veya uygulamalar çalıştır.", "Description[uk]": "Запускає зовнішні скрипти або програми для обробки текстових даних редактора або виконання інших потрібних дій.", "Description[x-test]": "xxRun external scripts or applications to manipulate the editor contents or do other arbitrary actions.xx", "Description[zh_CN]": "运行外部脚本或应用程序来处理编辑器内容或者执行其它任意动作。", - "Description[zh_TW]": "執行外部文稿或應用程式來運用編輯器內的內容,或是做各種動作。", "Icon": "system-run", "Id": "kdevexternalscript", "License": "GPL", "Name": "External Scripts", - "Name[ar]": "سكرِبتات خارجيّة", - "Name[bg]": "Външни скриптове", - "Name[bs]": "Spoljnje skripte", "Name[ca@valencia]": "Scripts externs", "Name[ca]": "Scripts externs", "Name[cs]": "Externí skripty", - "Name[da]": "Eksterne scripts", "Name[de]": "Externe Skripte", - "Name[el]": "Εξωτερικά σενάρια", "Name[en_GB]": "External Scripts", "Name[es]": "Scripts externos", "Name[et]": "Välised skriptid", "Name[fr]": "Scripts externes", "Name[gl]": "Scripts externos", - "Name[hu]": "Külső parancsfájlok", "Name[it]": "Script esterni", - "Name[kk]": "Сыртқы скрипттер", "Name[nb]": "Eksterne skripter", - "Name[nds]": "Extern Skripten", "Name[nl]": "Externe scripts", "Name[pl]": "Zewnętrzne skrypty", "Name[pt]": "Programas Externos", "Name[pt_BR]": "Scripts externos", "Name[ru]": "Внешние сценарии", "Name[sk]": "Externé skripty", "Name[sl]": "Zunanji skripti", "Name[sv]": "Externa skript", "Name[tr]": "Dış Betikler", - "Name[ug]": "سىرتقى قوليازما پروگرامما", "Name[uk]": "Зовнішні скрипти", "Name[x-test]": "xxExternal Scriptsxx", "Name[zh_CN]": "外部脚本", - "Name[zh_TW]": "外部文稿", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IPlugin" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/filemanager/kdevfilemanager.json b/plugins/filemanager/kdevfilemanager.json index 6c030e38aa..c326fd7389 100644 --- a/plugins/filemanager/kdevfilemanager.json +++ b/plugins/filemanager/kdevfilemanager.json @@ -1,102 +1,87 @@ { "KPlugin": { "Authors": [ { "Name": "Alexander Dymo", "Name[ca@valencia]": "Alexander Dymo", "Name[ca]": "Alexander Dymo", "Name[cs]": "Alexander Dymo", "Name[de]": "Alexander Dymo", - "Name[el]": "Alexander Dymo", "Name[en_GB]": "Alexander Dymo", "Name[es]": "Alexander Dymo", "Name[et]": "Alexander Dymo", "Name[fr]": "Alexander Dymo", "Name[gl]": "Alexander Dymo", "Name[it]": "Alexander Dymo", "Name[nl]": "Alexander Dymo", "Name[nn]": "Alexander Dymo", "Name[pl]": "Alexander Dymo", "Name[pt]": "Alexander Dymo", "Name[pt_BR]": "Alexander Dymo", "Name[ru]": "Александр Дымо", "Name[sk]": "Alexander Dymo", "Name[sl]": "Alexander Dymo", "Name[sv]": "Alexander Dymo", "Name[tr]": "Alexander Dymo", "Name[uk]": "Олександр Димо", "Name[x-test]": "xxAlexander Dymoxx", "Name[zh_CN]": "Alexander Dymo" } ], "Category": "Core", "Description": "This plugin brings a filemanager to KDevelop.", - "Description[ar]": "تجلب هذه الملحقة مدير ملفّات إلى مطوّرك.", "Description[ca@valencia]": "Aquest connector proporciona un gestor de fitxers al KDevelop.", "Description[ca]": "Aquest connector proporciona un gestor de fitxers al KDevelop.", "Description[cs]": "Tento modul do KDevelop přináší správce souborů.", "Description[de]": "Diese Modul fügt eine Dateiverwaltung zu KDevelop hinzu.", - "Description[el]": "Αυτό το πρόσθετο ενσωματώνει έναν διαχειριστή αρχείων στο KDevelop.", "Description[en_GB]": "This plugin brings a filemanager to KDevelop.", "Description[es]": "Este complemento proporciona un gestor de archivos a KDevelop.", "Description[et]": "See plugin võimaldab kasutada KDevelopis failihaldurit.", "Description[fr]": "Ce module apporte un gestionnaire de fichiers à KDevelop", "Description[gl]": "Este complemento incorpora un xestor de ficheiros en KDevelop.", "Description[it]": "Questa estensione porta un gestore di file in KDevelop.", "Description[nl]": "Deze plugin brengt een bestandsbeheerder in KDevelop.", "Description[pl]": "Umożliwia zarządzanie plikami w KDevelop.", "Description[pt]": "Este 'plugin' traz um gestor de ficheiros para o KDevelop.", "Description[pt_BR]": "Este plugin provê um gerenciador de arquivos ao KDevelop.", "Description[sk]": "Tento plugin prináša správcu súborov do KDevelop.", "Description[sl]": "Ta vstavek v KDevelop vgradi upravljalnika datotek", "Description[sv]": "Insticksprogrammet integrerar en filhanterare i KDevelop.", "Description[tr]": "Bu eklenti KDevelop için bir dosya yöneticisi sağlar.", "Description[uk]": "За допомогою цього додатка можна отримати доступ до менеджера файлів у KDevelop.", "Description[x-test]": "xxThis plugin brings a filemanager to KDevelop.xx", "Description[zh_CN]": "此插件为 KDevelop 提供了一个文件管理器。", - "Description[zh_TW]": "此外掛程式讓 KDevelop 可使用檔案管理員。", "Icon": "system-file-manager", "Id": "kdevfilemanager", "License": "GPL", "Name": "KDE File Manager Integration", - "Name[ar]": "تكامل مدير ملفّات كدي", - "Name[bg]": "Интегриране на файлов манипулатор на KDE", - "Name[bs]": "KDe menadžer integracije datoteke", "Name[ca@valencia]": "Integració del gestor de fitxers del KDE", "Name[ca]": "Integració del gestor de fitxers del KDE", "Name[cs]": "Integrace správce souborů pro KDE", - "Name[da]": "Integration af KDE filhåndtering", "Name[de]": "Integration der KDE-Dateiverwaltung", - "Name[el]": "Ενσωμάτωση διαχειριστή αρχείων του KDE", "Name[en_GB]": "KDE File Manager Integration", "Name[es]": "Integración del gestor de archivos de KDE", - "Name[et]": "KDE lõimimine failihalduriga", "Name[fr]": "Intégration du gestionnaire de fichiers de KDE", "Name[gl]": "Integración co xestor de ficheiros de KDE", - "Name[hu]": "KDE fájlkezelő integráció", "Name[it]": "Integrazione gestore di file di KDE", - "Name[kk]": "KDE файл менеджерімен біріктіру", "Name[nb]": "Integrasjon med KDE filbehandler", - "Name[nds]": "KDE-Dateiplegerünnerstütten", "Name[nl]": "KDE Bestandsbeheerder-integratie", "Name[pl]": "Wplecenie przeglądarki plików KDE", "Name[pt]": "Integração com o Gestor de Ficheiros do KDE", "Name[pt_BR]": "Integração com o gerenciador de arquivos do KDE", "Name[ru]": "Интеграция файлового менеджера KDE", "Name[sk]": "Integrácia správcu súborov KDE", "Name[sl]": "Vgradnja Upravljalnika datotek za KDE", "Name[sv]": "Integrering av KDE:s filhanterare", "Name[tr]": "KDE Dosya Yöneticisi Bütünleşmesi", - "Name[ug]": "KDE ھۆججەت باشقۇرغۇنىڭ يۈرۈشلەشتۈرۈلۇشى", "Name[uk]": "Інтеграція засобу керування файлами KDE", "Name[x-test]": "xxKDE File Manager Integrationxx", "Name[zh_CN]": "KDE 文件管理器整合", - "Name[zh_TW]": "KDE 檔案管理員整合", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/filetemplates/kdevfiletemplates.json b/plugins/filetemplates/kdevfiletemplates.json index cbaaabeef9..4aaae0f005 100644 --- a/plugins/filetemplates/kdevfiletemplates.json +++ b/plugins/filetemplates/kdevfiletemplates.json @@ -1,102 +1,91 @@ { "KPlugin": { "Authors": [ { "Name": "Alexander Dymo", "Name[ca@valencia]": "Alexander Dymo", "Name[ca]": "Alexander Dymo", "Name[cs]": "Alexander Dymo", "Name[de]": "Alexander Dymo", - "Name[el]": "Alexander Dymo", "Name[en_GB]": "Alexander Dymo", "Name[es]": "Alexander Dymo", "Name[et]": "Alexander Dymo", "Name[fr]": "Alexander Dymo", "Name[gl]": "Alexander Dymo", "Name[it]": "Alexander Dymo", "Name[nl]": "Alexander Dymo", "Name[nn]": "Alexander Dymo", "Name[pl]": "Alexander Dymo", "Name[pt]": "Alexander Dymo", "Name[pt_BR]": "Alexander Dymo", "Name[ru]": "Александр Дымо", "Name[sk]": "Alexander Dymo", "Name[sl]": "Alexander Dymo", "Name[sv]": "Alexander Dymo", "Name[tr]": "Alexander Dymo", "Name[uk]": "Олександр Димо", "Name[x-test]": "xxAlexander Dymoxx", "Name[zh_CN]": "Alexander Dymo" } ], "Category": "Core", "Description": "Manages templates for source files", - "Description[ar]": "تدير قوالب الملفّات المصدريّة", "Description[ca@valencia]": "Gestiona les plantilles dels fitxers de codi font", "Description[ca]": "Gestiona les plantilles dels fitxers de codi font", "Description[cs]": "Spravuje šablony pro zdrojové soubory", "Description[de]": "Verwaltung von Vorlagen für Quelltextdateien", - "Description[el]": "Διαχειρίζεται templates για πηγαία αρχεία", "Description[en_GB]": "Manages templates for source files", "Description[es]": "Gestiona plantillas para archivos de código fuente", "Description[et]": "Lähtekoodi failide mallide haldamine", "Description[fr]": "Gère les modèles de fichiers sources", "Description[gl]": "Xestiona modelos para os ficheiros de fonte", "Description[it]": "Gestisce i modelli per i file sorgente", "Description[nl]": "Beheert sjablonen voor broncodebestanden", "Description[pl]": "Zarządza szablonami dla plików źródłowych", "Description[pt]": "Gere os modelos de ficheiros de código", "Description[pt_BR]": "Gerencia os modelos dos arquivos de código", "Description[sk]": "Spravuje šablóny pre zdrojové súbory", "Description[sl]": "Upravlja predloge za datoteke z izvorno kodo", "Description[sv]": "Hanterar mallar för källkodsfiler", "Description[tr]": "Kaynak dosyaları için şablonları yönetir", "Description[uk]": "Керування шаблонами для початкових файлів коду", "Description[x-test]": "xxManages templates for source filesxx", "Description[zh_CN]": "管理源文件的模板", - "Description[zh_TW]": "管理源碼檔的樣本", "Icon": "code-class", "Id": "kdevfiletemplates", "License": "GPL", "Name": "File Templates Configuration", - "Name[ar]": "ضبط قوالب الملفّات", - "Name[bs]": "Konfiguracija predložaka datoteka", "Name[ca@valencia]": "Configuració de les plantilles de fitxer", "Name[ca]": "Configuració de les plantilles de fitxer", "Name[cs]": "Nastavení šablon souborů", - "Name[da]": "Konfiguration af filskabeloner", "Name[de]": "Einrichtung der Dateivorlagen", - "Name[el]": "Διαμόρφωση αρχείων templates", "Name[en_GB]": "File Templates Configuration", "Name[es]": "Configuración de plantillas de archivos", "Name[et]": "Failimallide seadistamine", "Name[fr]": "Configuration des modèles de fichiers", "Name[gl]": "Configuración dos modelos de ficheiros", - "Name[hu]": "Fájl sablonok beállítása", "Name[it]": "Configurazione dei file dei modelli", - "Name[kk]": "Файл үлгілерді баптау", "Name[nb]": "Oppsett av fil-maler", "Name[nl]": "Configuratie van sjabloonbestanden", "Name[pl]": "Ustawienia szablonów plików", "Name[pt]": "Configuração dos Modelos de Ficheiros", "Name[pt_BR]": "Configuração dos modelos de arquivos", "Name[ru]": "Настройка шаблонов файлов", "Name[sk]": "Nastavenie šablón súborov", "Name[sl]": "Nastavitev predlog dokumentov", "Name[sv]": "Inställning av filmallar", "Name[tr]": "Dosya Şablon Yapılandırması", "Name[uk]": "Налаштовування шаблонів файлів", "Name[x-test]": "xxFile Templates Configurationxx", "Name[zh_CN]": "文件模板配置", - "Name[zh_TW]": "檔案樣本設定", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.ITemplateProvider" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/filetemplates/kdevfiletemplates.knsrc b/plugins/filetemplates/kdevfiletemplates.knsrc index 29916787bf..1d9c6a02a5 100644 --- a/plugins/filetemplates/kdevfiletemplates.knsrc +++ b/plugins/filetemplates/kdevfiletemplates.knsrc @@ -1,29 +1,28 @@ [KNewStuff3] Name=File Templates (SDK) Name[ca]=Plantilles de fitxer (SDK) Name[ca@valencia]=Plantilles de fitxer (SDK) Name[cs]=Šablony souborů (SDK) Name[de]=Dateivorlagen (SDK) -Name[el]=Αρχεία templates (SDK) Name[en_GB]=File Templates (SDK) Name[es]=Plantillas de archivos (SDK) Name[et]=Failimallid (SDK) Name[fr]=Modèles de fichiers (SDK) Name[gl]=Modelos de ficheiros (SDK) Name[it]=Modelli di file (SDK) Name[nl]=Bestandssjablonen (SDK) Name[pl]=Szablony plików (SDK) Name[pt]=Modelos de Ficheiros (SDK) Name[pt_BR]=Modelos de arquivos (SDK) Name[sk]=Súborové šablóny (SDK) Name[sl]=Predloge datotek (SDK) Name[sv]=Filmallar (SDK) Name[tr]=Dosya Şablonları (SDK) Name[uk]=Шаблони файлів (SDK) Name[x-test]=xxFile Templates (SDK)xx Name[zh_CN]=文件模板 (SDK) ProvidersUrl=https://download.kde.org/ocs/providers.xml Categories=KDevelop File Template TargetDir=kdevfiletemplates/templates Uncompress=never diff --git a/plugins/flatpak/flatpak-template/flatpak-builder-manifest.desktop b/plugins/flatpak/flatpak-template/flatpak-builder-manifest.desktop index 38eb5520eb..05c6bf8a9a 100644 --- a/plugins/flatpak/flatpak-template/flatpak-builder-manifest.desktop +++ b/plugins/flatpak/flatpak-template/flatpak-builder-manifest.desktop @@ -1,112 +1,106 @@ [General] Name=Flatpak Builder Manifest Name[ca]=Manifest del constructor Flatpak Name[ca@valencia]=Manifest del constructor Flatpak Name[de]=Flatpak Builder Manifest -Name[el]=Flatpak Builder Manifest Name[en_GB]=Flatpak Builder Manifest Name[es]=Manifiesto de construcción de Flatpak Name[et]=Flatpaki ehitaja manifest Name[fr]=Manifest de construction Flatpak Name[gl]=Manifesto de construtor de Flatpak Name[it]=Manifesto di Flatpak Builder Name[nl]=Flatpak Builder Manifest Name[pl]=Manifest budowniczego Flatpak -Name[pt]=Manifesto de Construção do Flatpak +Name[pt]=Manifesto de Compilação do Flatpak Name[pt_BR]=Manifesto do construtor do Flatpak Name[sk]=Flatpak Builder Manifest -Name[sl]=Izgrajevalni manifest Flatpak Name[sv]=Flatpak-byggmanifest Name[tr]=Flatpak Oluşturucu Bildirgesi Name[uk]=Маніфест збирання Flatpak Name[x-test]=xxFlatpak Builder Manifestxx Name[zh_CN]=Flatpak 构建器清单文件 Comment=A file to configure how a project is built by flatpak Comment[ca]=Un fitxer per a configurar com serà construït un projecte per Flatpak Comment[ca@valencia]=Un fitxer per a configurar com serà construït un projecte per Flatpak Comment[de]=Eine Datei mit Einstellungen, um ein Projekt durch Flatpak zu erstellen -Comment[el]=Ένα αρχείο για το πώς ένα έργο έχει κατασκευαστεί με το flatpack Comment[en_GB]=A file to configure how a project is built by flatpak Comment[es]=Un archivo para configurar cómo construye flatpak un proyecto Comment[et]=Fail, millega saad seadistada, kuidas Flatpak ehitab projekti Comment[fr]=Un fichier pour configurer comment un projet est construit par Flatpak Comment[gl]=Un ficheiro para configurar como Flatpak constrúe un proxecto. Comment[it]=Un file per configurare come il progetto viene generato da flatpak Comment[nl]=Een bestand om te configureren hoe een project wordt gebouwd door flatpak Comment[pl]=Plik do ustawienia sposobu budowania przez flatpak -Comment[pt]=Um ficheiro para configurar como é compilado um projecto pelo Flatpak +Comment[pt]=Um ficheiro para configurar como um projecto é compilado pelo Flatpak Comment[pt_BR]=Um arquivo para configurar como um projeto é compilado pelo Flatpak Comment[sk]=Súbor na nastavenie, ako sa projekt zostaví cez flatpak -Comment[sl]=Datoteka, s katero lahko nastavite način izgradnje projekta s strani flatpak-a Comment[sv]=En fil för att ställa in hur ett projekt byggs av Flatpak Comment[tr]=Bir projenin, flatpak tarafından nasıl yapılandırılacağını belirten dosya Comment[uk]=Файл для налаштовування способу збирання проєкту за допомогою flatpak Comment[x-test]=xxA file to configure how a project is built by flatpakxx Comment[zh_CN]=配置 flatpak 如何构建项目的文件 Category=Flatpak/Builder Manifest Language=JSON Language[ca]=JSON Language[ca@valencia]=JSON Language[cs]=JSON Language[de]=JSON -Language[el]=JSON Language[en_GB]=JSON Language[es]=JSON Language[et]=JSON Language[fr]=JSON Language[gl]=JSON Language[it]=JSON Language[nb]=JSON Language[nl]=JSON Language[nn]=JSON Language[pl]=JSON Language[pt]=JSON Language[pt_BR]=JSON Language[se]=JSON Language[sk]=JSON -Language[sl]=JSON Language[sv]=JSON Language[tr]=JSON Language[uk]=JSON Language[x-test]=xxJSONxx Language[zh_CN]=JSON Files=Implementation OptionsFile=options.kcfg [Implementation] Name=Implementation Name[bs]=Implementacija Name[ca]=Implementació Name[ca@valencia]=Implementació Name[cs]=Implementace Name[da]=Implementering Name[de]=Implementation Name[el]=Υλοποίηση Name[en_GB]=Implementation Name[es]=Implementación Name[et]=Teostus Name[fi]=Toteutus Name[fr]=Implémentation Name[gl]=Implementación Name[hu]=Megvalósítás Name[it]=Implementazione Name[kk]=Іске асыруы Name[nb]=Implementering Name[nds]=Ümsetten Name[nl]=Implementatie Name[pl]=Implementacja Name[pt]=Implementação Name[pt_BR]=Implementação Name[ru]=Реализация Name[sk]=Implementácia Name[sl]=Izvedba Name[sv]=Implementering Name[tr]=Gerçekleme Name[ug]=ئەمەلگە ئاشۇرۇش Name[uk]=Реалізація Name[x-test]=xxImplementationxx Name[zh_CN]=接口 Name[zh_TW]=實作 File=flatpakbuilder.json OutputFile={{ name }}.json diff --git a/plugins/flatpak/flatpakruntime.cpp b/plugins/flatpak/flatpakruntime.cpp index 4c21aa14a7..c33525b952 100644 --- a/plugins/flatpak/flatpakruntime.cpp +++ b/plugins/flatpak/flatpakruntime.cpp @@ -1,269 +1,271 @@ /* Copyright 2017 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "flatpakruntime.h" #include "flatpakplugin.h" #include "debug_flatpak.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; template static T kTransform(const Q& list, W func) { T ret; ret.reserve(list.size()); for (auto it = list.constBegin(), itEnd = list.constEnd(); it!=itEnd; ++it) ret += func(*it); return ret; } static KJob* createExecuteJob(const QStringList &program, const QString &title, const QUrl &wd = {}, bool checkExitCode = true) { auto* process = new OutputExecuteJob; process->setProperties(OutputExecuteJob::DisplayStdout | OutputExecuteJob::DisplayStderr); process->setExecuteOnHost(true); process->setJobName(title); process->setWorkingDirectory(wd); process->setCheckExitCode(checkExitCode); *process << program; return process; } KJob* FlatpakRuntime::createBuildDirectory(const KDevelop::Path &buildDirectory, const KDevelop::Path &file, const QString &arch) { return createExecuteJob(QStringList{QStringLiteral("flatpak-builder"), QLatin1String("--arch=")+arch, QStringLiteral("--build-only"), buildDirectory.toLocalFile(), file.toLocalFile() }, i18n("Flatpak"), file.parent().toUrl()); } FlatpakRuntime::FlatpakRuntime(const KDevelop::Path &buildDirectory, const KDevelop::Path &file, const QString &arch) : KDevelop::IRuntime() , m_file(file) , m_buildDirectory(buildDirectory) , m_arch(arch) { refreshJson(); } FlatpakRuntime::~FlatpakRuntime() { } void FlatpakRuntime::refreshJson() { const auto doc = config(); const QString sdkName = doc[QLatin1String("sdk")].toString(); const QString runtimeVersion = doc.value(QLatin1String("runtime-version")).toString(); const QString usedRuntime = sdkName + QLatin1Char('/') + m_arch + QLatin1Char('/') + runtimeVersion; m_sdkPath = KDevelop::Path(QLatin1String("/var/lib/flatpak/runtime/") + usedRuntime + QLatin1String("/active/files")); qCDebug(FLATPAK) << "flatpak runtime path..." << name() << m_sdkPath; Q_ASSERT(QFile::exists(m_sdkPath.toLocalFile())); m_finishArgs = kTransform(doc[QLatin1String("finish-args")].toArray(), [](const QJsonValue& val){ return val.toString(); }); } void FlatpakRuntime::setEnabled(bool /*enable*/) { } void FlatpakRuntime::startProcess(QProcess* process) const { //Take any environment variables specified in process and pass through to flatpak. QStringList env_args; - for(QString env_var: process->processEnvironment().toStringList()) { + const QStringList env_vars = process->processEnvironment().toStringList(); + for (const QString& env_var : env_vars) { env_args << QLatin1String("--env=") + env_var; } const QStringList args = m_finishArgs + env_args + QStringList{QStringLiteral("build"), QStringLiteral("--talk-name=org.freedesktop.DBus"), m_buildDirectory.toLocalFile(), process->program()} << process->arguments(); process->setProgram(QStringLiteral("flatpak")); process->setArguments(args); qCDebug(FLATPAK) << "starting qprocess" << process->program() << process->arguments(); process->start(); } void FlatpakRuntime::startProcess(KProcess* process) const { //Take any environment variables specified in process and pass through to flatpak. QStringList env_args; - for(QString env_var: process->processEnvironment().toStringList()) { + const QStringList env_vars = process->processEnvironment().toStringList(); + for (const QString& env_var : env_vars) { env_args << QLatin1String("--env=") + env_var; } process->setProgram(QStringList{QStringLiteral("flatpak")} << m_finishArgs << env_args << QStringList{QStringLiteral("build"), QStringLiteral("--talk-name=org.freedesktop.DBus"), m_buildDirectory.toLocalFile() } << process->program()); qCDebug(FLATPAK) << "starting kprocess" << process->program().join(QLatin1Char(' ')); process->start(); } KJob* FlatpakRuntime::rebuild() { QDir(m_buildDirectory.toLocalFile()).removeRecursively(); auto job = createBuildDirectory(m_buildDirectory, m_file, m_arch); refreshJson(); return job; } QList FlatpakRuntime::exportBundle(const QString &path) const { const auto doc = config(); QTemporaryDir* dir = new QTemporaryDir(QDir::tempPath()+QLatin1String("/flatpak-tmp-repo")); if (!dir->isValid() || doc.isEmpty()) { qCWarning(FLATPAK) << "Couldn't export:" << path << dir->isValid() << dir->path() << doc.isEmpty(); return {}; } const QString name = doc[QLatin1String("id")].toString(); QStringList args = m_finishArgs; if (doc.contains(QLatin1String("command"))) args << QLatin1String("--command=")+doc[QLatin1String("command")].toString(); const QString title = i18n("Bundling"); const QList jobs = { createExecuteJob(QStringList{QStringLiteral("flatpak"), QStringLiteral("build-finish"), m_buildDirectory.toLocalFile()} << args, title, {}, false), createExecuteJob(QStringList{QStringLiteral("flatpak"), QStringLiteral("build-export"), QLatin1String("--arch=")+m_arch, dir->path(), m_buildDirectory.toLocalFile()}, title), createExecuteJob(QStringList{QStringLiteral("flatpak"), QStringLiteral("build-bundle"), QLatin1String("--arch=")+m_arch, dir->path(), path, name }, title) }; connect(jobs.last(), &QObject::destroyed, jobs.last(), [dir]() { delete dir; }); return jobs; } QString FlatpakRuntime::name() const { return QStringLiteral("%1 - %2").arg(m_arch, m_file.lastPathSegment()); } KJob * FlatpakRuntime::executeOnDevice(const QString& host, const QString &path) const { const QString name = config()[QLatin1String("id")].toString(); const QString destPath = QStringLiteral("/tmp/kdevelop-test-app.flatpak"); const QString replicatePath = QStringLiteral("/tmp/replicate.sh"); const QString localReplicatePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kdevflatpak/replicate.sh")); const QString title = i18n("Run on Device"); const QList jobs = exportBundle(path) << QList { createExecuteJob({QStringLiteral("scp"), path, host+QLatin1Char(':')+destPath}, title), createExecuteJob({QStringLiteral("scp"), localReplicatePath, host+QLatin1Char(':')+replicatePath}, title), createExecuteJob({QStringLiteral("ssh"), host, QStringLiteral("flatpak"), QStringLiteral("install"), QStringLiteral("--user"), QStringLiteral("--bundle"), QStringLiteral("-y"), destPath}, title), createExecuteJob({QStringLiteral("ssh"), host, QStringLiteral("bash"), replicatePath, QStringLiteral("plasmashell"), QStringLiteral("flatpak"), QStringLiteral("run"), name }, title), }; return new KDevelop::ExecuteCompositeJob( parent(), jobs ); } QJsonObject FlatpakRuntime::config(const KDevelop::Path& path) { QFile f(path.toLocalFile()); if (!f.open(QIODevice::ReadOnly)) { qCWarning(FLATPAK) << "couldn't open" << path; return {}; } QJsonParseError error; auto doc = QJsonDocument::fromJson(f.readAll(), &error); if (error.error) { qCWarning(FLATPAK) << "couldn't parse" << path << error.errorString(); return {}; } return doc.object(); } QJsonObject FlatpakRuntime::config() const { return config(m_file); } Path FlatpakRuntime::pathInHost(const KDevelop::Path& runtimePath) const { KDevelop::Path ret = runtimePath; if (!runtimePath.isLocalFile()) { return ret; } const auto prefix = runtimePath.segments().at(0); if (prefix == QLatin1String("usr")) { const auto relpath = KDevelop::Path(QStringLiteral("/usr")).relativePath(runtimePath); ret = Path(m_sdkPath, relpath); } else if (prefix == QLatin1String("app")) { const auto relpath = KDevelop::Path(QStringLiteral("/app")).relativePath(runtimePath); ret = Path(m_buildDirectory, QLatin1String("/active/files/") + relpath); } qCDebug(FLATPAK) << "path in host" << runtimePath << ret; return ret; } Path FlatpakRuntime::pathInRuntime(const KDevelop::Path& localPath) const { KDevelop::Path ret = localPath; if (m_sdkPath.isParentOf(localPath)) { const auto relpath = m_sdkPath.relativePath(localPath); ret = Path(Path(QStringLiteral("/usr")), relpath); } else { const Path bdfiles(m_buildDirectory, QStringLiteral("/active/files")); if (bdfiles.isParentOf(localPath)) { const auto relpath = bdfiles.relativePath(localPath); ret = Path(Path(QStringLiteral("/app")), relpath); } } qCDebug(FLATPAK) << "path in runtime" << localPath << ret; return ret; } QString FlatpakRuntime::findExecutable(const QString& executableName) const { QStringList rtPaths; auto envPaths = getenv(QByteArrayLiteral("PATH")).split(':'); std::transform(envPaths.begin(), envPaths.end(), std::back_inserter(rtPaths), [this](QByteArray p) { return pathInHost(Path(QString::fromLocal8Bit(p))).toLocalFile(); }); return QStandardPaths::findExecutable(executableName, rtPaths); } QByteArray FlatpakRuntime::getenv(const QByteArray& varname) const { if (varname == "KDEV_DEFAULT_INSTALL_PREFIX") return "/app"; return qgetenv(varname.constData()); } KDevelop::Path FlatpakRuntime::buildPath() const { auto file = m_file; file.setLastPathSegment(QStringLiteral(".flatpak-builder")); file.addPath(QStringLiteral("kdevelop")); return file; } diff --git a/plugins/flatpak/kdevflatpak.json b/plugins/flatpak/kdevflatpak.json index 1bc154fa16..f05d5602c2 100644 --- a/plugins/flatpak/kdevflatpak.json +++ b/plugins/flatpak/kdevflatpak.json @@ -1,90 +1,85 @@ { "KPlugin": { "Authors": [ { "Email": "aleixpol@kde.org", "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Runtimes", "Description": "Exposes Flatpak runtimes", "Description[ca@valencia]": "Exposa el temps d'execució del Flatpak", "Description[ca]": "Exposa el temps d'execució del Flatpak", "Description[de]": "Stellt Flatpak-Laufzeitumgebungen bereit", - "Description[el]": "Εμφανίζει εκτελέσεις του flatpak", "Description[en_GB]": "Exposes Flatpak runtimes", "Description[es]": "Expone bibliotecas en tiempo de ejecución de Flatpak", "Description[et]": "Flatpaki käitusaegade näitamine", "Description[fr]": "Expose les exécutifs Flatpak", "Description[gl]": "Expón os executábeis de Flatpak.", "Description[it]": "Espone i runtime di Flatpak", "Description[nl]": "Toont Flatpak runtimes", "Description[pl]": "Udostępnia biblioteki uruchomieniowe Flatpaka", - "Description[pt]": "Expõe as bibliotecas do Flatpak", + "Description[pt]": "Expõe os ambientes de execução do Flatpak", "Description[pt_BR]": "Expõe as bibliotecas do Flatpak", "Description[sk]": "Vystavuje Flatpak runtimes", - "Description[sl]": "Izpostavi izvajalne knjižnice za Flatpak", "Description[sv]": "Exponerar Flatpak-körtidsprogram", "Description[tr]": "Flatpak çalışma zamanlarını gösterir", "Description[uk]": "Надає доступ до середовищ виконання Flatpak", "Description[x-test]": "xxExposes Flatpak runtimesxx", "Description[zh_CN]": "暴露 Flatpak 运行时", "Icon": "kdevelop", "Id": "kdevflatpak", "License": "GPL", "Name": "Flatpak Support", "Name[ca@valencia]": "Implementació del Flatpak", "Name[ca]": "Implementació del Flatpak", "Name[cs]": "Podpora pro Flatpak", "Name[de]": "Flatpak-Unterstützung", - "Name[el]": "Υποστήριξη flatpak", "Name[en_GB]": "Flatpak Support", "Name[es]": "Implementación de Flatpak", - "Name[et]": "Flatpaki toetus", "Name[fr]": "Prise en charge de Flatpak", "Name[gl]": "Compatibilidade con Flatpak", "Name[it]": "Supporto per Flatpak", "Name[nl]": "Ondersteuning van Flatpak", "Name[pl]": "Obsługa Flatpak", "Name[pt]": "Suporte para o Flatpak", "Name[pt_BR]": "Suporte ao Flatpak", "Name[sk]": "Podpora Flatpak", "Name[sl]": "Podpora za Flatpak", "Name[sv]": "Flatpak-stöd", "Name[tr]": "Flatpak Desteği", "Name[uk]": "Підтримка Flatpak", "Name[x-test]": "xxFlatpak Supportxx", "Name[zh_CN]": "Flatpak 支持", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/gdb/kdevgdb.json b/plugins/gdb/kdevgdb.json index 680d008052..d383eaba8b 100644 --- a/plugins/gdb/kdevgdb.json +++ b/plugins/gdb/kdevgdb.json @@ -1,71 +1,66 @@ { "KPlugin": { "Category": "Debugging", "Description": "This plugin provides a frontend for GDB, a source-level debugger for C, C++ and more.", - "Description[bs]": "Ovaj dodatak pruža link za GDB, ispravljač grešaka izvornog nivoa za C, C++ i druge.", "Description[ca@valencia]": "Aquest connector proveeix d'un frontal per GDB, un depurador quant a codi per C, C++ i més llenguatges.", "Description[ca]": "Aquest connector proveeix d'un frontal per GDB, un depurador quant a codi per C, C++ i més llenguatges.", "Description[de]": "Dieses Modul stellt eine Oberfläche für GDB zur Verfügung. GDB ist ein Debugger für C, C++ und weitere Sprachen.", - "Description[el]": "Αυτό το πρόσθετο παρέχει ένα περιβάλλον χρήσης για το GDB, έναν διορθωτή σφαλμάτων πηγαίου κώδικα για C, C++ και άλλες.", "Description[en_GB]": "This plugin provides a frontend for GDB, a source-level debugger for C, C++ and more.", "Description[es]": "Este complemento proporciona una interfaz para GDB, un depurador de código fuente para C, C++ y otros lenguajes.", - "Description[et]": "See plugin pakub GDB - C, C++ jm lähtekoodi tasandil siluri - kasutajaliidest.", + "Description[et]": "See plugin pakub GDB kasutajaliidest. GDB on C, C++ ja veel mitme keele lähtekoodi tasandil tegutsev siluja.", "Description[fi]": "Tämä liitännäinen tarjoaa GDB-käyttöliittymän, lähdekooditason virheenpaikantimen C-, C++-kielille ja paljon muuta", "Description[fr]": "Ce module fournit une interface pour GDB, un débogueur niveau source pour le C, C++ et plus.", "Description[gl]": "Este complemento fornece unha interface para GDB, un depurador a nivel do código fonte para C, C++ e outros.", "Description[it]": "Questa estensione fornisce un'interfaccia per GDB, un debugger a livello sorgente per C, C++ e altro.", "Description[nl]": "Deze plugin levert een frontend voor GDB, een broncodedebugger voor C, C++ en meer.", "Description[pl]": "Udostępnia interfejs do GDB, programu diagnostycznego dla C, C++ i innych języków.", "Description[pt]": "Este 'plugin' oferece uma interface para o GDB, um depurador de código para C, C++, entre outros.", "Description[pt_BR]": "Este plugin oferece uma interface para o GDB, um depurador em nível de código-fonte para C, C++, entre outros.", "Description[sk]": "Tento modul poskytuje rozhranie pre GDB, zdrojový debuger pre C, C++ a ďalšie.", "Description[sl]": "Ta vstavek prinaša začelje za GDB, razhroščevalnik na ravni izvorne kode za C, C++ in več.", "Description[sv]": "Insticksprogrammet tillhandahåller ett gränssnitt till GDB, en källkodsavlusare för C, C++ med mera.", "Description[tr]": "Bu eklenti, C, C++ ve daha fazlası için bir kaynak düzeyinde hata ayıklayıcı olan GDB için bir önuç sağlar.", "Description[uk]": "Цей додаток надає можливість користуватися клієнтським інтерфейсом GDB, зневадника вихідного коду на C, C++ тощо.", "Description[x-test]": "xxThis plugin provides a frontend for GDB, a source-level debugger for C, C++ and more.xx", "Description[zh_CN]": "此插件提供了 GDB 前端,GDB 是一个源码级的 C、C++ 等多语言的调试器。", "Icon": "kdevelop", "Id": "kdevgdb", "License": "GPL", "Name": "GDB Support", - "Name[bs]": "GDB podrška", "Name[ca@valencia]": "Implementació del GDB", "Name[ca]": "Implementació del GDB", "Name[cs]": "Podpora GDB", "Name[de]": "Unterstützung für GDB", - "Name[el]": "Υποστήριξη GDB", "Name[en_GB]": "GDB Support", "Name[es]": "Implementación de GDB", "Name[et]": "GDB toetus", "Name[fi]": "GDB-tuki", "Name[fr]": "Prise en charge de GDB", "Name[gl]": "Compatibilidade con GDB", - "Name[hu]": "GDB támogatás", "Name[it]": "Supporto per GDB", "Name[nl]": "Ondersteuning voor GDB", "Name[nn]": "GDB-støtte", "Name[pl]": "Obsługa GDB", "Name[pt]": "Suporte para o GDB", "Name[pt_BR]": "Suporte ao GDB", "Name[ru]": "Поддержка GDB", "Name[sk]": "Podpora GDB", "Name[sl]": "Podpora za GDB", "Name[sv]": "GDB-stöd", "Name[tr]": "GDB Desteği", "Name[uk]": "Підтримка GDB", "Name[x-test]": "xxGDB Supportxx", "Name[zh_CN]": "GDB 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IExecutePlugin" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IStatus" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/gdb/unittests/test_gdb.cpp b/plugins/gdb/unittests/test_gdb.cpp index 6f5173fc41..7b8590f061 100644 --- a/plugins/gdb/unittests/test_gdb.cpp +++ b/plugins/gdb/unittests/test_gdb.cpp @@ -1,2122 +1,2122 @@ /* Copyright 2009 Niko Sams Copyright 2013 Vlas Puhov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "test_gdb.h" #include "debugsession.h" #include "gdbframestackmodel.h" #include "mi/micommand.h" #include "mi/milexer.h" #include "mi/miparser.h" #include "tests/debuggers-tests-config.h" #include "tests/testhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SKIP_IF_ATTACH_FORBIDDEN() \ do { \ if (KDevMI::isAttachForbidden(__FILE__, __LINE__)) \ return; \ } while(0) using KDevelop::AutoTestShell; using KDevMI::findExecutable; using KDevMI::findSourceFile; using KDevMI::findFile; namespace KDevMI { namespace GDB { void GdbTest::initTestCase() { AutoTestShell::init(); KDevelop::TestCore::initialize(KDevelop::Core::NoUi); m_iface = KDevelop::ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))->extension(); Q_ASSERT(m_iface); } void GdbTest::cleanupTestCase() { KDevelop::TestCore::shutdown(); } void GdbTest::init() { //remove all breakpoints - so we can set our own in the test KConfigGroup breakpoints = KSharedConfig::openConfig()->group("breakpoints"); breakpoints.writeEntry("number", 0); breakpoints.sync(); KDevelop::BreakpointModel* m = KDevelop::ICore::self()->debugController()->breakpointModel(); m->removeRows(0, m->rowCount()); KDevelop::VariableCollection *vc = KDevelop::ICore::self()->debugController()->variableCollection(); for (int i=0; i < vc->watches()->childCount(); ++i) { delete vc->watches()->child(i); } vc->watches()->clear(); } class WritableEnvironmentProfileList : public KDevelop::EnvironmentProfileList { public: explicit WritableEnvironmentProfileList(KConfig* config) : EnvironmentProfileList(config) {} using EnvironmentProfileList::variables; using EnvironmentProfileList::saveSettings; using EnvironmentProfileList::removeProfile; }; class TestLaunchConfiguration : public KDevelop::ILaunchConfiguration { public: explicit TestLaunchConfiguration(const QUrl& executable = findExecutable(QStringLiteral("debuggee_debugee")), const QUrl& workingDirectory = QUrl()) { qDebug() << "FIND" << executable; c = KSharedConfig::openConfig(); c->deleteGroup("launch"); cfg = c->group("launch"); cfg.writeEntry("isExecutable", true); cfg.writeEntry("Executable", executable); cfg.writeEntry("Working Directory", workingDirectory); } const KConfigGroup config() const override { return cfg; } KConfigGroup config() override { return cfg; }; QString name() const override { return QStringLiteral("Test-Launch"); } KDevelop::IProject* project() const override { return nullptr; } KDevelop::LaunchConfigurationType* type() const override { return nullptr; } KConfig* rootConfig() { return c.data(); } private: KConfigGroup cfg; KSharedConfigPtr c; }; class TestFrameStackModel : public GdbFrameStackModel { Q_OBJECT public: explicit TestFrameStackModel(DebugSession* session) : GdbFrameStackModel(session), fetchFramesCalled(0), fetchThreadsCalled(0) {} int fetchFramesCalled; int fetchThreadsCalled; void fetchFrames(int threadNumber, int from, int to) override { fetchFramesCalled++; GdbFrameStackModel::fetchFrames(threadNumber, from, to); } void fetchThreads() override { fetchThreadsCalled++; GdbFrameStackModel::fetchThreads(); } }; class TestDebugSession : public DebugSession { Q_OBJECT public: TestDebugSession() : DebugSession() { setSourceInitFile(false); setAutoDisableASLR(false); m_frameStackModel = new TestFrameStackModel(this); KDevelop::ICore::self()->debugController()->addSession(this); } QUrl url() { return currentUrl(); } int line() { return currentLine(); } TestFrameStackModel* frameStackModel() const override { return m_frameStackModel; } private: TestFrameStackModel* m_frameStackModel; }; class TestWaiter { public: TestWaiter(DebugSession * session_, const char * condition_, const char * file_, int line_) : session(session_) , condition(condition_) , file(file_) , line(line_) { stopWatch.start(); } bool waitUnless(bool ok) { if (ok) { qDebug() << "Condition " << condition << " reached in " << file << ':' << line; return false; } if (stopWatch.elapsed() > 5000) { QTest::qFail(qPrintable(QString("Timeout before reaching condition %0").arg(condition)), file, line); return false; } QTest::qWait(100); if (!session) { QTest::qFail(qPrintable(QString("Session ended without reaching condition %0").arg(condition)), file, line); return false; } return true; } private: QTime stopWatch; QPointer session; const char * condition; const char * file; int line; }; #define WAIT_FOR_STATE(session, state) \ do { if (!waitForState((session), (state), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR_STATE_AND_IDLE(session, state) \ do { if (!waitForState((session), (state), __FILE__, __LINE__, true)) return; } while (0) #define WAIT_FOR(session, condition) \ do { \ TestWaiter w((session), #condition, __FILE__, __LINE__); \ while (w.waitUnless((condition))) /* nothing */ ; \ } while(0) #define COMPARE_DATA(index, expected) \ do { if(!compareData((index), (expected), __FILE__, __LINE__)) return; } while (0) bool compareData(const QModelIndex& index, const QString& expected, const char *file, int line) { QString s = index.model()->data(index, Qt::DisplayRole).toString(); if (s != expected) { QTest::qFail(qPrintable(QString("'%0' didn't match expected '%1' in %2:%3") .arg(s, expected, file).arg(line)), file, line); return false; } return true; } static const QString debugeeFileName = findSourceFile(QStringLiteral("debugee.cpp")); KDevelop::BreakpointModel* breakpoints() { return KDevelop::ICore::self()->debugController()->breakpointModel(); } void GdbTest::testStdOut() { auto *session = new TestDebugSession; QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); TestLaunchConfiguration cfg; session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); { QCOMPARE(outputSpy.count(), 1); QList arguments = outputSpy.takeFirst(); QCOMPARE(arguments.count(), 1); QCOMPARE(arguments.first().toStringList(), QStringList() << "Hello, world!" << "Hello"); } } void GdbTest::testEnvironmentSet() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeechoenv"))); cfg.config().writeEntry("EnvironmentGroup", "GdbTestGroup"); WritableEnvironmentProfileList envProfiles(cfg.rootConfig()); envProfiles.removeProfile(QStringLiteral("GdbTestGroup")); auto &envs = envProfiles.variables(QStringLiteral("GdbTestGroup")); envs[QStringLiteral("VariableA")] = QStringLiteral("-A' \" complex --value"); envs[QStringLiteral("VariableB")] = QStringLiteral("-B' \" complex --value"); envProfiles.saveSettings(cfg.rootConfig()); QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { - QList arguments = outputSpy.takeFirst(); + const QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << "-A' \" complex --value" << "-B' \" complex --value"); } void GdbTest::testBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); } void GdbTest::testDisableBreakpoint() { //Description: We must stop only on the third breakpoint int firstBreakLine=28; int secondBreakLine=23; int thirdBreakLine=24; int fourthBreakLine=31; auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint *b; b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), firstBreakLine); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //this is needed to emulate debug from GUI. If we are in edit mode, the debugSession doesn't exist. KDevelop::ICore::self()->debugController()->breakpointModel()->blockSignals(true); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), secondBreakLine); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //all disabled breakpoints were added KDevelop::Breakpoint * thirdBreak = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), thirdBreakLine); KDevelop::ICore::self()->debugController()->breakpointModel()->blockSignals(false); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), thirdBreak->line()); //disable existing breakpoint thirdBreak->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //add another disabled breakpoint b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), fourthBreakLine); QTest::qWait(300); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); QTest::qWait(300); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testChangeLocationBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 27); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); QTest::qWait(100); b->setLine(28); QTest::qWait(100); session->run(); QTest::qWait(100); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 28); QTest::qWait(500); breakpoints()->setData(breakpoints()->index(0, KDevelop::Breakpoint::LocationColumn), QString(debugeeFileName+":30")); QCOMPARE(b->line(), 29); QTest::qWait(100); QCOMPARE(b->line(), 29); session->run(); QTest::qWait(100); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testDeleteBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; QCOMPARE(breakpoints()->rowCount(), 0); //add breakpoint before startDebugging breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); QCOMPARE(breakpoints()->rowCount(), 1); breakpoints()->removeRow(0); QCOMPARE(breakpoints()->rowCount(), 0); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 22); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testPendingBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile(QStringLiteral("debugeeqt.cpp"))), 10); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::PendingState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testUpdateBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; // breakpoint 1: real line 29: foo(); KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); QCOMPARE(breakpoints()->rowCount(), 1); session->startDebugging(&cfg, m_iface); // breakpoint 2: real line 32: const char *x = "Hello"; //insert custom command as user might do it using GDB console session->addCommand(new MI::UserCommand(MI::NonMI, "break "+debugeeFileName+":32")); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop at breakpoint 1, with custom command handled QCOMPARE(session->currentLine(), 28); // check breakpoint 2 got picked up QCOMPARE(breakpoints()->rowCount(), 2); b = breakpoints()->breakpoint(1); QCOMPARE(b->url(), QUrl::fromLocalFile(debugeeFileName)); QCOMPARE(b->line(), 31); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); // stop at breakpoint 2 QCOMPARE(session->currentLine(), 31); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testIgnoreHitsBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b1 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); b1->setIgnoreHits(1); KDevelop::Breakpoint * b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 22); session->startDebugging(&cfg, m_iface); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b2->hitCount() == 1); b2->setIgnoreHits(1); session->run(); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b1->hitCount() == 1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testConditionBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 39); b->setCondition(QStringLiteral("x[0] == 'H'")); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 23); b->setCondition(QStringLiteral("i==2")); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); session->startDebugging(&cfg, m_iface); WAIT_FOR(session, session->state() == DebugSession::PausedState && session->line() == 24); b->setCondition(QStringLiteral("i == 0")); QTest::qWait(100); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 39); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testBreakOnWriteBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); breakpoints()->addWatchpoint(QStringLiteral("i")); QTest::qWait(100); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // line 23: ++i; int j = i; session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testBreakOnWriteWithConditionBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); KDevelop::Breakpoint *b = breakpoints()->addWatchpoint(QStringLiteral("i")); b->setCondition(QStringLiteral("i==2")); QTest::qWait(100); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // line 23: ++i; int j = i; session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testBreakOnReadBreakpoint() { /* test disabled because of gdb bug: http://sourceware.org/bugzilla/show_bug.cgi?id=10136 TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint *b = breakpoints()->addReadWatchpoint("foo::i"); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); */ } void GdbTest::testBreakOnReadBreakpoint2() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); breakpoints()->addReadWatchpoint(QStringLiteral("i")); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // ++i session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // int j = i session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); if(session->line() == 22) { // some GDB versions break 3 times on this line session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); } QCOMPARE(session->line(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testBreakOnAccessBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); breakpoints()->addAccessWatchpoint(QStringLiteral("i")); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // line 23: ++i (read) session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // line 23: ++i (write) session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 22); // line 23: int j = i (read) session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testInsertBreakpointWhileRunning() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile(QStringLiteral("debugeeslow.cpp")); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::ActiveState); QTest::qWait(2000); qDebug() << "adding breakpoint"; KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 30); // ++i; QTest::qWait(500); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); QCOMPARE(session->line(), 30); // ++i; b->setDeleted(); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testInsertBreakpointWhileRunningMultiple() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile(QStringLiteral("debugeeslow.cpp")); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::ActiveState); QTest::qWait(2000); qDebug() << "adding breakpoint"; KDevelop::Breakpoint *b1 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 29); // static int i=0; KDevelop::Breakpoint *b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 30); // ++i; QTest::qWait(500); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); QCOMPARE(session->line(), 29); // static int i=0; session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); QCOMPARE(session->line(), 30); // ++i; b1->setDeleted(); b2->setDeleted(); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testInsertBreakpointFunctionName() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testManualBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); breakpoints()->removeRows(0, 1); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->rowCount(), 0); session->addCommand(MI::NonMI, QStringLiteral("break debugee.cpp:23")); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->rowCount(), 1); KDevelop::Breakpoint* b = breakpoints()->breakpoint(0); QCOMPARE(b->line(), 22); session->addCommand(MI::NonMI, QStringLiteral("disable 2")); session->addCommand(MI::NonMI, QStringLiteral("condition 2 i == 1")); session->addCommand(MI::NonMI, QStringLiteral("ignore 2 1")); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->enabled(), false); QCOMPARE(b->condition(), QString("i == 1")); QCOMPARE(b->ignoreHits(), 1); session->addCommand(MI::NonMI, QStringLiteral("delete 2")); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->rowCount(), 0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testShowStepInSource() { auto *session = new TestDebugSession; QSignalSpy showStepInSourceSpy(session, &TestDebugSession::showStepInSource); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 29); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); { QCOMPARE(showStepInSourceSpy.count(), 3); QList arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().toUrl(), QUrl::fromLocalFile(debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 29); arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().toUrl(), QUrl::fromLocalFile(debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 22); arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().toUrl(), QUrl::fromLocalFile(debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 23); } } void GdbTest::testStack() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo"); QCOMPARE(stackModel->rowCount(tIdx), 2); QCOMPARE(stackModel->columnCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "foo"); COMPARE_DATA(stackModel->index(0, 2, tIdx), debugeeFileName+":23"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(1, 2, tIdx), debugeeFileName+":29"); session->stepOut(); WAIT_FOR_STATE(session, DebugSession::PausedState); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 1); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), debugeeFileName+":30"); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testStackFetchMore() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeerecursion"))); QString fileName = findSourceFile(QStringLiteral("debugeerecursion.cpp")); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->frameStackModel()->fetchFramesCalled, 1); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo"); QCOMPARE(stackModel->rowCount(tIdx), 21); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "foo"); COMPARE_DATA(stackModel->index(0, 2, tIdx), fileName+":26"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "foo"); COMPARE_DATA(stackModel->index(1, 2, tIdx), fileName+":24"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "foo"); COMPARE_DATA(stackModel->index(2, 2, tIdx), fileName+":24"); COMPARE_DATA(stackModel->index(19, 0, tIdx), "19"); COMPARE_DATA(stackModel->index(20, 0, tIdx), "20"); stackModel->fetchMoreFrames(); QTest::qWait(200); QCOMPARE(stackModel->fetchFramesCalled, 2); QCOMPARE(stackModel->rowCount(tIdx), 41); COMPARE_DATA(stackModel->index(20, 0, tIdx), "20"); COMPARE_DATA(stackModel->index(21, 0, tIdx), "21"); COMPARE_DATA(stackModel->index(22, 0, tIdx), "22"); COMPARE_DATA(stackModel->index(39, 0, tIdx), "39"); COMPARE_DATA(stackModel->index(40, 0, tIdx), "40"); stackModel->fetchMoreFrames(); QTest::qWait(200); QCOMPARE(stackModel->fetchFramesCalled, 3); QCOMPARE(stackModel->rowCount(tIdx), 121); COMPARE_DATA(stackModel->index(40, 0, tIdx), "40"); COMPARE_DATA(stackModel->index(41, 0, tIdx), "41"); COMPARE_DATA(stackModel->index(42, 0, tIdx), "42"); COMPARE_DATA(stackModel->index(119, 0, tIdx), "119"); COMPARE_DATA(stackModel->index(120, 0, tIdx), "120"); stackModel->fetchMoreFrames(); QTest::qWait(200); QCOMPARE(stackModel->fetchFramesCalled, 4); QCOMPARE(stackModel->rowCount(tIdx), 299); COMPARE_DATA(stackModel->index(120, 0, tIdx), "120"); COMPARE_DATA(stackModel->index(121, 0, tIdx), "121"); COMPARE_DATA(stackModel->index(122, 0, tIdx), "122"); COMPARE_DATA(stackModel->index(298, 0, tIdx), "298"); COMPARE_DATA(stackModel->index(298, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(298, 2, tIdx), fileName+":30"); stackModel->fetchMoreFrames(); //nothing to fetch, we are at the end QTest::qWait(200); QCOMPARE(stackModel->fetchFramesCalled, 4); QCOMPARE(stackModel->rowCount(tIdx), 299); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testStackDeactivateAndActive() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); session->stepOut(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(200); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 1); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), debugeeFileName+":30"); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testStackSwitchThread() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeethreads"))); QString fileName = findSourceFile(QStringLiteral("debugeethreads.cpp")); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 40); // t3.start(); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QVERIFY(stackModel->rowCount() > 2); QModelIndex tIdx = stackModel->index(0,0); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 1); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), fileName+":41"); // QThread::usleep(500000); tIdx = stackModel->index(1,0); QVERIFY(stackModel->data(tIdx).toString().startsWith("#2 at ")); stackModel->setCurrentThread(2); QTest::qWait(200); int rows = stackModel->rowCount(tIdx); QVERIFY(rows > 3); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testAttach() { SKIP_IF_ATTACH_FORBIDDEN(); #ifdef Q_OS_FREEBSD // Despite successful attach GDB MI spits out a error message "Can't allocate registers". This gets caught by KDevMI layer and gets interpreted as error. // Upstream PR: https://sourceware.org/bugzilla/show_bug.cgi?id=23464 QSKIP("GDB on FreeBSD produces an unexpected error message, on which KDevelop chokes"); #endif QString fileName = findSourceFile(QStringLiteral("debugeeslow.cpp")); KProcess debugeeProcess; debugeeProcess << QStringLiteral("nice") << findExecutable(QStringLiteral("debuggee_debugeeslow")).toLocalFile(); debugeeProcess.start(); QVERIFY(debugeeProcess.waitForStarted()); QTest::qWait(100); auto *session = new TestDebugSession; session->attachToProcess(debugeeProcess.pid()); WAIT_FOR_STATE(session, DebugSession::PausedState); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 39); // } after foo(); QTest::qWait(100); session->run(); QTest::qWait(2000); WAIT_FOR_STATE(session, DebugSession::PausedState); if (session->line() < 39 || session->line() < 40) { QCOMPARE(session->line(), 39); } session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testManualAttach() { SKIP_IF_ATTACH_FORBIDDEN(); #ifdef Q_OS_FREEBSD // Despite successful attach GDB MI spits out a error message "Can't allocate registers". This gets caught by KDevMI layer and gets interpreted as error. // Upstream PR: https://sourceware.org/bugzilla/show_bug.cgi?id=23464 QSKIP("GDB on FreeBSD produces an unexpected error message, on which KDevelop chokes"); #endif KProcess debugeeProcess; debugeeProcess << QStringLiteral("nice") << findExecutable(QStringLiteral("debuggee_debugeeslow")).toLocalFile(); debugeeProcess.start(); QVERIFY(debugeeProcess.waitForStarted()); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; cfg.config().writeEntry(Config::RemoteGdbRunEntry, QUrl::fromLocalFile(findFile(GDB_SRC_DIR, QStringLiteral("unittests/gdb_script_empty")))); QVERIFY(session->startDebugging(&cfg, m_iface)); session->addCommand(MI::NonMI, QStringLiteral("attach %0").arg(debugeeProcess.pid())); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); QTest::qWait(2000); // give the slow inferior some extra time to run WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testCoreFile() { QFileInfo f(QStringLiteral("core")); f.setCaching(false); // don't cache information if (f.exists()) { QVERIFY(QFile::remove(f.canonicalFilePath())); } KProcess debugeeProcess; debugeeProcess.setOutputChannelMode(KProcess::MergedChannels); debugeeProcess << QStringLiteral("bash") << QStringLiteral("-c") << "ulimit -c unlimited; " + findExecutable(QStringLiteral("debuggee_crash")).toLocalFile(); debugeeProcess.start(); debugeeProcess.waitForFinished(); qDebug() << "Debuggee output:\n" << debugeeProcess.readAll(); bool coreFileFound = f.exists(); if (!coreFileFound) { // Try to use coredumpctl auto coredumpctl = QStandardPaths::findExecutable(QStringLiteral("coredumpctl")); if (!coredumpctl.isEmpty()) { KProcess::execute(coredumpctl, {"-1", "-o", f.absoluteFilePath(), "dump", "debuggee_crash"}, 5000); // coredumpctl seems to create an empty file "core" even if no cores can be delivered // (like when run inside docker containers as on KDE CI or with kernel.core_pattern=|/dev/null) // so also check for size != 0 coreFileFound = f.exists() && (f.size() > 0); } } if (!coreFileFound) QSKIP("no core dump found, check your system configuration (see /proc/sys/kernel/core_pattern).", SkipSingle); auto *session = new TestDebugSession; session->examineCoreFile(findExecutable(QStringLiteral("debuggee_crash")), QUrl::fromLocalFile(f.canonicalFilePath())); TestFrameStackModel *stackModel = session->frameStackModel(); WAIT_FOR_STATE(session, DebugSession::StoppedState); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo"); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } KDevelop::VariableCollection *variableCollection() { return KDevelop::ICore::self()->debugController()->variableCollection(); } void GdbTest::testVariablesLocals() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 22); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); QCOMPARE(variableCollection()->rowCount(), 2); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 2); COMPARE_DATA(variableCollection()->index(0, 0, i), "i"); COMPARE_DATA(variableCollection()->index(0, 1, i), "0"); COMPARE_DATA(variableCollection()->index(1, 0, i), "j"); // COMPARE_DATA(variableCollection()->index(1, 1, i), "1"); // j is not initialized yet session->run(); QTest::qWait(1000); WAIT_FOR_STATE(session, DebugSession::PausedState); COMPARE_DATA(variableCollection()->index(0, 0, i), "i"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); COMPARE_DATA(variableCollection()->index(1, 0, i), "j"); COMPARE_DATA(variableCollection()->index(1, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testVariablesLocalsStruct() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); QModelIndex i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); int structIndex = 0; for(int j=0; j<3; ++j) { if (variableCollection()->index(j, 0, i).data().toString() == QLatin1String("ts")) { structIndex = j; } } COMPARE_DATA(variableCollection()->index(structIndex, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(structIndex, 1, i), "{...}"); QModelIndex ts = variableCollection()->index(structIndex, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, ts), "..."); variableCollection()->expanded(ts); QTest::qWait(100); COMPARE_DATA(variableCollection()->index(0, 0, ts), "a"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "0"); COMPARE_DATA(variableCollection()->index(1, 0, ts), "b"); COMPARE_DATA(variableCollection()->index(1, 1, ts), "1"); COMPARE_DATA(variableCollection()->index(2, 0, ts), "c"); COMPARE_DATA(variableCollection()->index(2, 1, ts), "2"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); COMPARE_DATA(variableCollection()->index(structIndex, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(structIndex, 1, i), "{...}"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testVariablesWatches() { auto *session = new TestDebugSession; KDevelop::ICore::self()->debugController()->variableCollection()->variableWidgetShown(); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("ts")); QTest::qWait(300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(0, 1, i), "{...}"); QModelIndex ts = variableCollection()->index(0, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, ts), "..."); variableCollection()->expanded(ts); QTest::qWait(100); COMPARE_DATA(variableCollection()->index(0, 0, ts), "a"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "0"); COMPARE_DATA(variableCollection()->index(1, 0, ts), "b"); COMPARE_DATA(variableCollection()->index(1, 1, ts), "1"); COMPARE_DATA(variableCollection()->index(2, 0, ts), "c"); COMPARE_DATA(variableCollection()->index(2, 1, ts), "2"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(100); COMPARE_DATA(variableCollection()->index(0, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(0, 1, i), "{...}"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testVariablesWatchesQuotes() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); TestLaunchConfiguration cfg; // the unquoted string (the actual content): t\"t // quoted string (what we would write as a c string): "t\\\"t" // written in source file: R"("t\\\"t")" const QString testString(QStringLiteral("t\\\"t")); // the actual content const QString quotedTestString(QStringLiteral(R"("t\\\"t")")); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); variableCollection()->watches()->add(quotedTestString); //just a constant string QTest::qWait(300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), quotedTestString); COMPARE_DATA(variableCollection()->index(0, 1, i), "[" + QString::number(testString.length() + 1) + "]"); QModelIndex testStr = variableCollection()->index(0, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, testStr), "..."); variableCollection()->expanded(testStr); QTest::qWait(100); int len = testString.length(); for (int ind = 0; ind < len; ind++) { COMPARE_DATA(variableCollection()->index(ind, 0, testStr), QString::number(ind)); QChar c = testString.at(ind); QString value = QString::number(c.toLatin1()) + " '"; if (c == '\\') value += QLatin1String("\\\\"); else if (c == '\'') value += QLatin1String("\\'"); else value += c; value += QLatin1String("'"); COMPARE_DATA(variableCollection()->index(ind, 1, testStr), value); } COMPARE_DATA(variableCollection()->index(len, 0, testStr), QString::number(len)); COMPARE_DATA(variableCollection()->index(len, 1, testStr), "0 '\\000'"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testVariablesWatchesTwoSessions() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("ts")); QTest::qWait(300); QModelIndex ts = variableCollection()->index(0, 0, variableCollection()->index(0, 0)); variableCollection()->expanded(ts); QTest::qWait(100); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //check if variable is marked as out-of-scope QCOMPARE(variableCollection()->watches()->childCount(), 1); auto* v = qobject_cast(variableCollection()->watches()->child(0)); QVERIFY(v); QVERIFY(!v->inScope()); QCOMPARE(v->childCount(), 3); v = qobject_cast(v->child(0)); QVERIFY(!v->inScope()); //start a second debug session session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(300); QCOMPARE(variableCollection()->watches()->childCount(), 1); ts = variableCollection()->index(0, 0, variableCollection()->index(0, 0)); v = qobject_cast(variableCollection()->watches()->child(0)); QVERIFY(v); QVERIFY(v->inScope()); QCOMPARE(v->childCount(), 3); v = qobject_cast(v->child(0)); QVERIFY(v->inScope()); QCOMPARE(v->data(1, Qt::DisplayRole).toString(), QString::number(0)); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //check if variable is marked as out-of-scope v = qobject_cast(variableCollection()->watches()->child(0)); QVERIFY(!v->inScope()); QVERIFY(!dynamic_cast(v->child(0))->inScope()); } void GdbTest::testVariablesStopDebugger() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); session->stopDebugger(); QTest::qWait(300); } void GdbTest::testVariablesStartSecondSession() { QPointer session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QPointer session2 = new TestDebugSession; session2->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); QVERIFY(session2->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session2, DebugSession::PausedState); session2->run(); WAIT_FOR_STATE(session2, DebugSession::EndedState); } void GdbTest::testVariablesSwitchFrame() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 2); COMPARE_DATA(variableCollection()->index(0, 0, i), "i"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); COMPARE_DATA(variableCollection()->index(1, 0, i), "j"); stackModel->setCurrentFrame(1); QTest::qWait(200); i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); COMPARE_DATA(variableCollection()->index(2, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(2, 1, i), "1"); COMPARE_DATA(variableCollection()->index(3, 0, i), "argv"); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testVariablesQuicklySwitchFrame() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 2); COMPARE_DATA(variableCollection()->index(0, 0, i), "i"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); COMPARE_DATA(variableCollection()->index(1, 0, i), "j"); stackModel->setCurrentFrame(1); QTest::qWait(300); stackModel->setCurrentFrame(0); QTest::qWait(1); stackModel->setCurrentFrame(1); QTest::qWait(1); stackModel->setCurrentFrame(0); QTest::qWait(1); stackModel->setCurrentFrame(1); QTest::qWait(500); i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); QStringList locs; for (int j = 0; j < variableCollection()->rowCount(i); ++j) { locs << variableCollection()->index(j, 0, i).data().toString(); } QVERIFY(locs.contains("argc")); QVERIFY(locs.contains("argv")); QVERIFY(locs.contains("x")); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testSegfaultDebugee() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_crash"))); QString fileName = findSourceFile(QStringLiteral("debugeecrash.cpp")); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 23); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testSwitchFrameGdbConsole() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->currentFrame(), 0); stackModel->setCurrentFrame(1); QCOMPARE(stackModel->currentFrame(), 1); QTest::qWait(500); QCOMPARE(stackModel->currentFrame(), 1); session->addUserCommand(QStringLiteral("print x")); QTest::qWait(500); //currentFrame must not reset to 0; Bug 222882 QCOMPARE(stackModel->currentFrame(), 1); } //Bug 201771 void GdbTest::testInsertAndRemoveBreakpointWhileRunning() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile(QStringLiteral("debugeeslow.cpp")); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::ActiveState); QTest::qWait(2000); qDebug() << "adding breakpoint"; KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 30); // ++i; b->setDeleted(); WAIT_FOR_STATE(session, DebugSession::EndedState); } //Bug 274390 void GdbTest::testCommandOrderFastStepping() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeqt"))); breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); for(int i=0; i<20; i++) { session->stepInto(); } WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testPickupManuallyInsertedBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); session->addCommand(MI::NonMI, QStringLiteral("break debugee.cpp:32")); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); //wait for breakpoints update QCOMPARE(breakpoints()->breakpoints().count(), 2); QCOMPARE(breakpoints()->rowCount(), 2); KDevelop::Breakpoint *b = breakpoints()->breakpoint(1); QVERIFY(b); QCOMPARE(b->line(), 31); //we start with 0, gdb with 1 QCOMPARE(b->url().fileName(), QString("debugee.cpp")); } //Bug 270970 void GdbTest::testPickupManuallyInsertedBreakpointOnlyOnce() { auto *session = new TestDebugSession; //inject here, so it behaves similar like a command from .gdbinit QTemporaryFile configScript; configScript.open(); configScript.write(QStringLiteral("file %0\n").arg(findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile()).toLocal8Bit()); configScript.write("break debugee.cpp:32\n"); configScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::RemoteGdbConfigEntry, QUrl::fromLocalFile(configScript.fileName())); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(QStringLiteral("debugee.cpp")), 31); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testPickupCatchThrowOnlyOnce() { QTemporaryFile configScript; configScript.open(); configScript.write("catch throw\n"); configScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::RemoteGdbConfigEntry, QUrl::fromLocalFile(configScript.fileName())); for (int i = 0; i < 2; ++i) { auto* session = new TestDebugSession; QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::EndedState); } QCOMPARE(breakpoints()->rowCount(), 1); //one from kdevelop, one from runScript } void GdbTest::testRunGdbScript() { auto *session = new TestDebugSession; QTemporaryFile runScript; runScript.open(); runScript.write("file " + KShell::quoteArg(findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile()).toUtf8() + "\n"); runScript.write("break main\n"); runScript.write("run\n"); runScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::RemoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testRemoteDebug() { const QString gdbserverExecutable = QStandardPaths::findExecutable(QStringLiteral("gdbserver")); if (gdbserverExecutable.isEmpty()) { QSKIP("Skipping, gdbserver not available", SkipSingle); } auto *session = new TestDebugSession; QTemporaryFile shellScript(QDir::currentPath()+"/shellscript"); shellScript.open(); shellScript.write("gdbserver localhost:2345 " + findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile().toUtf8() + "\n"); shellScript.close(); shellScript.setPermissions(shellScript.permissions() | QFile::ExeUser); QFile::copy(shellScript.fileName(), shellScript.fileName()+"-copy"); //to avoid "Text file busy" on executing (why?) QTemporaryFile runScript(QDir::currentPath()+"/runscript"); runScript.open(); runScript.write("file " + findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile().toUtf8() + "\n"); runScript.write("target remote localhost:2345\n"); runScript.write("break debugee.cpp:30\n"); runScript.write("continue\n"); runScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::RemoteGdbShellEntry, QUrl::fromLocalFile((shellScript.fileName()+"-copy"))); grp.writeEntry(Config::RemoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); QFile::remove(shellScript.fileName()+"-copy"); } void GdbTest::testRemoteDebugInsertBreakpoint() { const QString gdbserverExecutable = QStandardPaths::findExecutable(QStringLiteral("gdbserver")); if (gdbserverExecutable.isEmpty()) { QSKIP("Skipping, gdbserver not available", SkipSingle); } auto *session = new TestDebugSession; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 29); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 35); QTemporaryFile shellScript(QDir::currentPath()+"/shellscript"); shellScript.open(); shellScript.write("gdbserver localhost:2345 " + findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile().toUtf8() + "\n"); shellScript.close(); shellScript.setPermissions(shellScript.permissions() | QFile::ExeUser); QFile::copy(shellScript.fileName(), shellScript.fileName()+"-copy"); //to avoid "Text file busy" on executing (why?) QTemporaryFile runScript(QDir::currentPath()+"/runscript"); runScript.open(); runScript.write("file " + findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile().toUtf8() + '\n'); runScript.write("target remote localhost:2345\n"); runScript.write("break debugee.cpp:30\n"); runScript.write("continue\n"); runScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::RemoteGdbShellEntry, QUrl::fromLocalFile(shellScript.fileName()+"-copy")); grp.writeEntry(Config::RemoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); QCOMPARE(breakpoints()->breakpoints().count(), 2); //one from kdevelop, one from runScript session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); QFile::remove(shellScript.fileName()+"-copy"); } void GdbTest::testRemoteDebugInsertBreakpointPickupOnlyOnce() { const QString gdbserverExecutable = QStandardPaths::findExecutable(QStringLiteral("gdbserver")); if (gdbserverExecutable.isEmpty()) { QSKIP("Skipping, gdbserver not available", SkipSingle); } auto *session = new TestDebugSession; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 35); QTemporaryFile shellScript(QDir::currentPath()+"/shellscript"); shellScript.open(); shellScript.write("gdbserver localhost:2345 "+findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile().toLatin1()+"\n"); shellScript.close(); shellScript.setPermissions(shellScript.permissions() | QFile::ExeUser); QFile::copy(shellScript.fileName(), shellScript.fileName()+"-copy"); //to avoid "Text file busy" on executing (why?) QTemporaryFile runScript(QDir::currentPath()+"/runscript"); runScript.open(); runScript.write("file "+findExecutable(QStringLiteral("debuggee_debugee")).toLocalFile().toLatin1()+"\n"); runScript.write("target remote localhost:2345\n"); runScript.write("break debugee.cpp:30\n"); runScript.write("continue\n"); runScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::RemoteGdbShellEntry, QUrl::fromLocalFile((shellScript.fileName()+"-copy"))); grp.writeEntry(Config::RemoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); QCOMPARE(breakpoints()->breakpoints().count(), 2); //one from kdevelop, one from runScript session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //************************** second session session = new TestDebugSession; QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); QCOMPARE(breakpoints()->breakpoints().count(), 2); //one from kdevelop, one from runScript session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); QFile::remove(shellScript.fileName()+"-copy"); } void GdbTest::testBreakpointWithSpaceInPath() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeespace"))); KConfigGroup grp = cfg.config(); QString fileName = findSourceFile(QStringLiteral("debugee space.cpp")); KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 20); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 20); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testBreakpointDisabledOnStart() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28) ->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 29); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 31); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 31); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testCatchpoint() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeexception"))); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile(QStringLiteral("debugeeexception.cpp"))), 29); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); TestFrameStackModel* fsModel = session->frameStackModel(); QCOMPARE(fsModel->currentFrame(), 0); QCOMPARE(session->line(), 29); session->addCommand(MI::NonMI, QStringLiteral("catch throw")); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QTest::qWait(1000); const QVector frames = fsModel->frames(fsModel->currentThread()); QVERIFY(frames.size() >= 2); // frame 0 is somewhere inside libstdc++ QCOMPARE(frames[1].file, QUrl::fromLocalFile(findSourceFile("debugeeexception.cpp"))); QCOMPARE(frames[1].line, 22); QCOMPARE(breakpoints()->rowCount(),2); QVERIFY(!breakpoints()->breakpoint(0)->location().isEmpty()); QVERIFY(!breakpoints()->breakpoint(1)->location().isEmpty()); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testThreadAndFrameInfo() { // Check if --thread is added to user commands auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeethreads"))); QString fileName = findSourceFile(QStringLiteral("debugeethreads.cpp")); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QSignalSpy outputSpy(session, &TestDebugSession::debuggerUserCommandOutput); session->addCommand(new MI::UserCommand(MI::ThreadInfo, QString())); session->addCommand(new MI::UserCommand(MI::StackListLocals, QStringLiteral("0"))); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // wait for command finish // outputs should be // 1. -thread-info // 2. ^done for thread-info // 3. -stack-list-locals // 4. ^done for -stack-list-locals QCOMPARE(outputSpy.count(), 4); QVERIFY(outputSpy.at(2).at(0).toString().contains(QLatin1String("--thread 1"))); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::parseBug304730() { MI::FileSymbol file; file.contents = QByteArray("^done,bkpt={" "number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"\",times=\"0\"," "original-location=\"/media/portable/Projects/BDSInpainting/PatchMatch/PatchMatch.hpp:231\"}," "{number=\"1.1\",enabled=\"y\",addr=\"0x081d84aa\"," "func=\"PatchMatch, 2u> >" "::Propagation(ForwardPropagationNeighbors)\"," "file=\"/media/portable/Projects/BDSInpainting/Drivers/../PatchMatch/PatchMatch.hpp\"," "fullname=\"/media/portable/Projects/BDSInpainting/PatchMatch/PatchMatch.hpp\",line=\"231\"}," "{number=\"1.2\",enabled=\"y\",addr=\"0x081d8ae2\"," "func=\"PatchMatch, 2u> >" "::Propagation(BackwardPropagationNeighbors)\"," "file=\"/media/portable/Projects/BDSInpainting/Drivers/../PatchMatch/PatchMatch.hpp\"," "fullname=\"/media/portable/Projects/BDSInpainting/PatchMatch/PatchMatch.hpp\",line=\"231\"}," "{number=\"1.3\",enabled=\"y\",addr=\"0x081d911a\"," "func=\"PatchMatch, 2u> >" "::Propagation(AllowedPropagationNeighbors)\"," "file=\"/media/portable/Projects/BDSInpainting/Drivers/../PatchMatch/PatchMatch.hpp\"," "fullname=\"/media/portable/Projects/BDSInpainting/PatchMatch/PatchMatch.hpp\",line=\"231\"}"); MI::MIParser parser; std::unique_ptr record(parser.parse(&file)); QVERIFY(record.get() != nullptr); } void GdbTest::testMultipleLocationsBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeemultilocbreakpoint"))); breakpoints()->addCodeBreakpoint(QStringLiteral("aPlusB")); //TODO check if the additional location breakpoint is added session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 19); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testBug301287() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("argc")); QTest::qWait(300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //start second debug session (same cfg) session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(300); i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testMultipleBreakpoint() { auto *session = new TestDebugSession; //there'll be about 3-4 breakpoints, but we treat it like one. TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeemultiplebreakpoint"))); KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QStringLiteral("debugeemultiplebreakpoint.cpp:52")); session->startDebugging(&c, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testRegularExpressionBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeemultilocbreakpoint"))); breakpoints()->addCodeBreakpoint(QStringLiteral("main")); session->startDebugging(&c, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); session->addCommand(MI::NonMI, QStringLiteral("rbreak .*aPl.*B")); QTest::qWait(100); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 3); session->addCommand(MI::BreakDelete, QString()); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testChangeBreakpointWhileRunning() { auto *session = new TestDebugSession; TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeeslow"))); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QStringLiteral("debugeeslow.cpp:30")); session->startDebugging(&c, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QVERIFY(session->currentLine() >= 29 && session->currentLine() <= 31 ); session->run(); WAIT_FOR_STATE(session, DebugSession::ActiveState); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //to make one loop QTest::qWait(2000); WAIT_FOR_STATE(session, DebugSession::ActiveState); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); QTest::qWait(100); WAIT_FOR_STATE(session, DebugSession::PausedState); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->run(); QTest::qWait(100); WAIT_FOR_STATE(session, DebugSession::EndedState); } void GdbTest::testDebugInExternalTerminal() { TestLaunchConfiguration cfg; const QStringList consoles { "konsole", "xterm", "xfce4-terminal", "gnome-terminal" }; for (const QString& console : consoles) { TestDebugSession* session = nullptr; if (QStandardPaths::findExecutable(console).isEmpty()) { continue; } session = new TestDebugSession(); cfg.config().writeEntry("External Terminal"/*ExecutePlugin::terminalEntry*/, console); cfg.config().writeEntry("Use External Terminal"/*ExecutePlugin::useTerminalEntry*/, true); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } } // see: https://bugs.kde.org/show_bug.cgi?id=339231 void GdbTest::testPathWithSpace() { auto* session = new TestDebugSession; auto debugee = findExecutable(QStringLiteral("path with space/debuggee_spacedebugee")); TestLaunchConfiguration c(debugee, KIO::upUrl(debugee)); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QStringLiteral("spacedebugee.cpp:30")); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); session->startDebugging(&c, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } bool GdbTest::waitForState(DebugSession *session, DebugSession::DebuggerState state, const char *file, int line, bool waitForIdle) { QPointer s(session); //session can get deleted in DebugController QTime stopWatch; stopWatch.start(); // legacy behavior for tests that implicitly may require waiting for idle, // but which were written before waitForIdle was added waitForIdle = waitForIdle || state != MIDebugSession::EndedState; while (s && (s->state() != state || (waitForIdle && s->debuggerStateIsOn(s_dbgBusy)))) { if (stopWatch.elapsed() > 5000) { qWarning() << "current state" << s->state() << "waiting for" << state; QTest::qFail(qPrintable(QString("Timeout before reaching state %0").arg(state)), file, line); return false; } QTest::qWait(20); } // NOTE: don't wait anymore after leaving the loop. Waiting re-enters event loop and // may change session state. if (!s && state != MIDebugSession::EndedState) { QTest::qFail(qPrintable(QString("Session ended before reaching state %0").arg(state)), file, line); return false; } qDebug() << "Reached state " << state << " in " << file << ':' << line; return true; } } // end of namespace GDB } // end of namespace KDevMI QTEST_MAIN(KDevMI::GDB::GdbTest) #include "test_gdb.moc" #include "moc_test_gdb.cpp" diff --git a/plugins/genericprojectmanager/kdevgenericmanager.json b/plugins/genericprojectmanager/kdevgenericmanager.json index ebf1d3a22d..a9e636751f 100644 --- a/plugins/genericprojectmanager/kdevgenericmanager.json +++ b/plugins/genericprojectmanager/kdevgenericmanager.json @@ -1,102 +1,88 @@ { "KPlugin": { "Authors": [ { "Name": "Milian Wolff", "Name[ca@valencia]": "Milian Wolff", "Name[ca]": "Milian Wolff", "Name[cs]": "Milian Wolff", "Name[de]": "Milian Wolff", - "Name[el]": "Milian Wolff", "Name[en_GB]": "Milian Wolff", "Name[es]": "Milian Wolff", "Name[et]": "Milian Wolff", "Name[fr]": "Milian Wolff", "Name[gl]": "Milian Wolff", "Name[it]": "Milian Wolff", "Name[nl]": "Milian Wolff", "Name[nn]": "Milian Wolff", "Name[pl]": "Milian Wolff", "Name[pt]": "Milian Wolff", "Name[pt_BR]": "Milian Wolff", "Name[ru]": "Milian Wolff", "Name[sk]": "Milian Wolff", "Name[sl]": "Milian Wolff", "Name[sv]": "Milian Wolff", "Name[tr]": "Milian Wolff", "Name[uk]": "Milian Wolff", "Name[x-test]": "xxMilian Wolffxx", "Name[zh_CN]": "Milian Wolff" } ], "Category": "Project Management", "Description": "Allow KDevelop to manage generic projects", "Description[ca@valencia]": "Permet al KDevelop gestionar projectes genèrics", "Description[ca]": "Permet al KDevelop gestionar projectes genèrics", "Description[cs]": "Umožní KDevelopu spravovat obecné projekty", "Description[de]": "Zum Verwalten allgemeiner Projekte in KDevelop", - "Description[el]": "Επιτρέπει στο KDevelop τη διαχείριση γενικευμένων έργων", "Description[en_GB]": "Allow KDevelop to manage generic projects", "Description[es]": "Permite que KDevelop gestione proyectos genéricos", - "Description[et]": "Võimaldab KDevelopil hallata üldisi projekte", "Description[fr]": "Permet à KDevelop de gérer des projets génériques", "Description[gl]": "Permítelle a KDevelop xestionar proxectos xenéricos", "Description[it]": "Permette a KDevelop di gestire progetti generici", "Description[nl]": "Sta toe dat KDevelop generieke projecten beheert", "Description[pl]": "Umożliwia zarządzanie zwykłymi projektami", "Description[pt]": "Permitir ao KDevelop gerir projectos genéricos", "Description[pt_BR]": "Permite que o KDevelop gerencie projetos genéricos", "Description[sk]": "Povoliť KDevelopu spravovať všeobecné projekty", "Description[sl]": "Omogoča, da KDevelop upravlja s splošnimi projekti", "Description[sv]": "Tillåter att KDevelop hanterar generella projekt", "Description[tr]": "KDevelop uygulamasının genel projeleri yönetmesine izin ver", "Description[uk]": "За допомогою цього додатка можна увімкнути керування загальними проєктами у KDevelop", "Description[x-test]": "xxAllow KDevelop to manage generic projectsxx", "Description[zh_CN]": "允许 KDevelop 管理常规工程", - "Description[zh_TW]": "讓 KDevelop 管理一般的專案", "Icon": "kdevelop", "Id": "KDevGenericManager", "Name": "Generic Project Manager", - "Name[bg]": "Най-общ манипулатор на проект", "Name[ca@valencia]": "Gestor de projectes genèric", "Name[ca]": "Gestor de projectes genèric", "Name[cs]": "Obecný správce projektů", - "Name[da]": "Håndtering af generisk projekt", "Name[de]": "Allgemeine Projektverwaltung", - "Name[el]": "Γενικευμένος διαχειριστής έργου", "Name[en_GB]": "Generic Project Manager", "Name[es]": "Gestor de proyectos genérico", - "Name[et]": "Üldine projektihaldur", "Name[fr]": "Gestionnaire de projets générique", "Name[gl]": "Xestor de proxectos xenérico", - "Name[hu]": "Általános projektkezelő", "Name[it]": "Gestore progetto generico", - "Name[kk]": "Жалпы жоба менеджері", "Name[nb]": "Generisk prosjektbehandler", - "Name[nds]": "Allmeen Projektpleger", "Name[nl]": "Generieke projectenbeheerder", - "Name[pa]": "ਆਮ ਪਰੋਜੈਕਟ ਮੈਨੇਜਰ", "Name[pl]": "Zarządzanie zwykłymi projektami", "Name[pt]": "Gestor de Projectos Genérico", "Name[pt_BR]": "Gerenciador de projetos genérico", "Name[ru]": "Управление произвольными проектами", "Name[sk]": "Všeobecný správca projektov", "Name[sl]": "Splošni upravljalnik projektov", "Name[sv]": "Generell projekthantering", "Name[tr]": "Genel Proje Yöneticisi", - "Name[ug]": "ئادەتتىكى قۇرۇلۇش باشقۇرغۇچ", "Name[uk]": "Звичайний засіб керування проєктом", "Name[x-test]": "xxGeneric Project Managerxx", "Name[zh_CN]": "常规工程管理器", - "Name[zh_TW]": "一般專案管理員", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-FileManager": "None", "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectFileManager" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/ghprovider/kdevghprovider.json b/plugins/ghprovider/kdevghprovider.json index e86eaf685b..9076c1c203 100644 --- a/plugins/ghprovider/kdevghprovider.json +++ b/plugins/ghprovider/kdevghprovider.json @@ -1,95 +1,90 @@ { "KPlugin": { "Authors": [ { "Name": "Miquel Sabaté", "Name[ca@valencia]": "Miquel Sabaté", "Name[ca]": "Miquel Sabaté", "Name[cs]": "Miquel Sabaté", "Name[de]": "Miquel Sabaté", - "Name[el]": "Miquel Sabaté", "Name[en_GB]": "Miquel Sabaté", "Name[es]": "Miquel Sabaté", "Name[et]": "Miquel Sabaté", "Name[fi]": "Miquel Sabaté", "Name[fr]": "Miquel Sabaté", "Name[gl]": "Miquel Sabaté", "Name[it]": "Miquel Sabaté", "Name[nl]": "Miquel Sabaté", "Name[nn]": "Miquel Sabaté", "Name[pl]": "Miquel Sabaté", "Name[pt]": "Miquel Sabaté", "Name[pt_BR]": "Miquel Sabaté", "Name[ru]": "Miquel Sabaté", "Name[sk]": "Miquel Sabaté", "Name[sl]": "Miquel Sabaté", "Name[sv]": "Miquel Sabaté", "Name[tr]": "Miquel Sabaté", "Name[uk]": "Miquel Sabaté", "Name[x-test]": "xxMiquel Sabatéxx", "Name[zh_CN]": "Miquel Sabaté" } ], "Category": "Utilities", "Description": "This plugin helps to obtain projects from GitHub", "Description[ca@valencia]": "Aquest connector ajuda a obtindre els projectes des de GitHub", "Description[ca]": "Aquest connector ajuda a obtenir els projectes des de GitHub", "Description[de]": "Dieses Modul hilft dabei, GitHub-Projekte zu beziehen", - "Description[el]": "Αυτό το πρόσθετο βοηθάει στη λήψη έργων από το Github", "Description[en_GB]": "This plugin helps to obtain projects from GitHub", "Description[es]": "Este complemento le ayuda a obtener proyectos de GitHub", - "Description[et]": "See plugin aitab hankida projekte Githubist", "Description[fr]": "Ce module aide à obtenir des projets depuis GitHub", "Description[gl]": "Este complemento permite obter proxectos de GitHub.", "Description[it]": "Questa estensione consente di ottenere progetti da GitHub", "Description[nl]": "Deze plug-in helpt om projecten uit Github te verkrijgen", "Description[pl]": "Pobiera projekty z GitHuba", "Description[pt]": "Este 'plugin' ajuda a obter projectos do GitHub", "Description[pt_BR]": "Este plugin ajuda a obter projetos a partir do GitHub", "Description[sk]": "Tento modul pomáha získavať projekty z GitHub", "Description[sl]": "Ta vstavek pomaga pri pridobivanju projektov iz GitHub-a", "Description[sv]": "Insticksprogrammet hjälper till att erhålla projekt från Github", "Description[tr]": "Bu eklenti Github'tan projeleri almanıza yardımcı olur", "Description[uk]": "За допомогою цього додатка можна отримувати проєкти з GitHub", "Description[x-test]": "xxThis plugin helps to obtain projects from GitHubxx", "Description[zh_CN]": "此插件帮助您从 GitHub 获取工程", "Icon": "kdevgh", "Id": "kdevghprovider", "License": "GPL", "Name": "GitHub Provider", "Name[ca@valencia]": "Proveïdor de GitHub", "Name[ca]": "Proveïdor de GitHub", "Name[cs]": "Poskytovatel GitHubu", "Name[de]": "GitHub-Provider", - "Name[el]": "Πάροχος github", "Name[en_GB]": "GitHub Provider", "Name[es]": "Proveedor de GitHub", - "Name[et]": "GitHubi pakkuja", "Name[fr]": "Fournisseur GitHub", "Name[gl]": "Fornecedor de GitHub", "Name[it]": "Fornitore GitHub", "Name[nl]": "Github-leverancier", "Name[nn]": "GitHub-tilbydar", "Name[pl]": "Dostawca GitHub", "Name[pt]": "Fornecedor do GitHub", "Name[pt_BR]": "Fornecedor do GitHub", "Name[sk]": "Poskytovateľ GitHub", "Name[sl]": "Ponudnik za GitHub", "Name[sv]": "Github-leverantör", "Name[tr]": "GitHub Sağlayıcı", "Name[uk]": "Надавач даних GitHub", "Name[x-test]": "xxGitHub Providerxx", "Name[zh_CN]": "GitHub 提供者", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IBasicVersionControl@kdevgit" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectProvider" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/git/kdevgit.json b/plugins/git/kdevgit.json index 3f40f5837d..b38c2fa7d5 100644 --- a/plugins/git/kdevgit.json +++ b/plugins/git/kdevgit.json @@ -1,104 +1,89 @@ { "KPlugin": { "Authors": [ { "Email": "powerfox@kde.ru", "Name": "Evgeniy Ivanov", "Name[ca@valencia]": "Evgeniy Ivanov", "Name[ca]": "Evgeniy Ivanov", "Name[cs]": "Evgeniy Ivanov", "Name[de]": "Evgeniy Ivanov", - "Name[el]": "Evgeniy Ivanov", "Name[en_GB]": "Evgeniy Ivanov", "Name[es]": "Evgeniy Ivanov", "Name[et]": "Evgeniy Ivanov", "Name[fr]": "Evgeniy Ivanov", "Name[gl]": "Evgeniy Ivanov", "Name[it]": "Evgeniy Ivanov", "Name[nl]": "Evgeniy Ivanov", "Name[nn]": "Evgeniy Ivanov", "Name[pl]": "Evgeniy Ivanov", "Name[pt]": "Evgeniy Ivanov", "Name[pt_BR]": "Evgeniy Ivanov", "Name[ru]": "Евгений Иванов", "Name[sk]": "Evgeniy Ivanov", "Name[sl]": "Evgeniy Ivanov", "Name[sv]": "Evgeniy Ivanov", "Name[tr]": "Evgeniy Ivanov", "Name[uk]": "Evgeniy Ivanov", "Name[x-test]": "xxEvgeniy Ivanovxx", "Name[zh_CN]": "Evgeniy Ivanov" } ], "Category": "Version Control", "Description": "This plugin integrates Git to KDevelop", - "Description[ar]": "تكامل هذه الملحقة Git بِمطوّرك", "Description[ca@valencia]": "Aquest connector integra el Git al KDevelop", "Description[ca]": "Aquest connector integra el Git al KDevelop", "Description[cs]": "Tento modul integruje podporu pro Git v KDevelop", "Description[de]": "Dieses Modul integriert Git in KDevelop.", - "Description[el]": "Αυτό το πρόσθετο ενσωματώνει το Git στο KDevelop", "Description[en_GB]": "This plugin integrates Git to KDevelop", "Description[es]": "Este complemento integra Git en KDevelop", "Description[et]": "See plugin lõimib Giti KDevelopiga", "Description[fr]": "Ce module externe intègre la gestion de Git dans KDevelop", "Description[gl]": "Este complemento integra Git en KDevelop", "Description[it]": "Questa estensione integra Git in KDevelop", "Description[nl]": "Deze plug-in integreert Git in KDevelop", "Description[pl]": "Wplata Git w KDevelop", "Description[pt]": "Este 'plugin' integra o Git no KDevelop", "Description[pt_BR]": "Este plugin integra o Git ao KDevelop", "Description[sk]": "Tento plugin integruje GIT do KDevelop.", "Description[sl]": "Ta vstavek v KDevelop vgradi Git", "Description[sv]": "Insticksprogrammet integrerar Git i KDevelop", "Description[tr]": "Bu eklenti Git uygulamasını KDevelop ile bütünleştirir", "Description[uk]": "Цей додаток інтегрує Git із KDevelop", "Description[x-test]": "xxThis plugin integrates Git to KDevelopxx", "Description[zh_CN]": "此插件对 KDevelop 整合 Git", - "Description[zh_TW]": "此外掛程式將 Git 整合進 KDevelop", "Icon": "git", "Id": "kdevgit", "License": "GPL", "Name": "Git Support", - "Name[ar]": "دعم Git", - "Name[bg]": "Поддръжка на Git", "Name[ca@valencia]": "Implementació de Git", "Name[ca]": "Implementació de Git", "Name[cs]": "Podpora Git", - "Name[da]": "Git-understøttelse", "Name[de]": "Git-Unterstützung", - "Name[el]": "Υποστήριξη Git", "Name[en_GB]": "Git Support", "Name[es]": "Implementación de Git", - "Name[et]": "Giti toetus", "Name[fr]": "Prise en charge de Git", - "Name[ga]": "Tacaíocht Git", "Name[gl]": "Compatibilidade con Git", "Name[it]": "Supporto per Git", - "Name[kk]": "Git қолдауы", - "Name[nds]": "Git-Ünnerstütten", "Name[nl]": "Git-ondersteuning", - "Name[pa]": "Git ਸਹਿਯੋਗ", "Name[pl]": "Obsługa Git", "Name[pt]": "Suporte para o Git", "Name[pt_BR]": "Suporte ao Git", "Name[sk]": "Podpora GIT", "Name[sl]": "Podpora za Git", "Name[sv]": "Git-stöd", "Name[tr]": "Git Desteği", - "Name[ug]": "Git قوللىشى", "Name[uk]": "Підтримка Git", "Name[x-test]": "xxGit Supportxx", "Name[zh_CN]": "Git 支持", - "Name[zh_TW]": "Git 支援", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.9" }, "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl", "org.kdevelop.IDistributedVersionControl" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/git/org.kde.kdevelop_git.desktop b/plugins/git/org.kde.kdevelop_git.desktop index 1e73734780..f002c16a88 100644 --- a/plugins/git/org.kde.kdevelop_git.desktop +++ b/plugins/git/org.kde.kdevelop_git.desktop @@ -1,69 +1,67 @@ [Desktop Entry] Type=Application Exec=kdevelop --ps --fetch %U MimeType=x-scheme-handler/git;x-scheme-handler/git+ssh; Icon=kdevelop Terminal=false Name=KDevelop (Fetch git Project) Name[ca]=KDevelop (obtenir un projecte de Git) Name[ca@valencia]=KDevelop (obtindre un projecte de Git) Name[de]=KDevelop (Git-Projekt holen) -Name[el]=KDevelop (Λήψη έργου από git) Name[en_GB]=KDevelop (Fetch git Project) Name[es]=KDevelop (obtener proyecto de git) Name[et]=KDevelop (giti projekti tõmbamine) Name[fr]=KDevelop (récupérer un projet Git) Name[gl]=KDevelop (obter un proxecto de Git) Name[it]=KDevelop (importa un progetto git) Name[nl]=KDevelop (git-project ophalen) Name[pl]=KDevelop (Pobierz projekt git) Name[pt]=KDevelop (Obter um Projecto do Git) Name[pt_BR]=KDevelop (Obter projeto do Git) -Name[sk]=KDevelop (Načítať git Projekt) -Name[sl]=KDevelop (pridobi projekt git) +Name[sk]=KDevelop (Stiahnuť Git Projekt) Name[sv]=KDevelop (hämta git-projekt) Name[uk]=KDevelop (отримати проєкт git) Name[x-test]=xxKDevelop (Fetch git Project)xx Name[zh_CN]=KDevelop (获取 Git 项目) GenericName=Integrated Development Environment GenericName[ar]=بيئة تطوير متكاملة GenericName[bs]=Integrisano razvojno okruženje GenericName[ca]=Entorn integrat de desenvolupament GenericName[ca@valencia]=Entorn integrat de desenvolupament GenericName[cs]=Integrované Vývojové Prostředí GenericName[da]=Integreret udviklingsmiljø (IDE) GenericName[de]=Integrierte Entwicklungsumgebung GenericName[el]=ολοκληρωμένο περιβάλλον ανάπτυξης GenericName[en_GB]=Integrated Development Environment GenericName[es]=Entorno de desarrollo integrado GenericName[et]=Integreeritud arenduskeskkond GenericName[fi]=Integroitu kehitysympäristö GenericName[fr]=Environnement de Développement Intégré GenericName[ga]=Timpeallacht Chomhtháite Fhorbartha GenericName[gl]=Entorno de desenvolvemento integrado GenericName[hne]=एकीकृत डेवलपमेंट वातावरन GenericName[hu]=Integrált fejlesztői környezet GenericName[it]=Ambiente di sviluppo integrato GenericName[ja]=統合開発環境 GenericName[kk]=Біріктірілген құрастыру ортасы GenericName[km]=Development Environment ដែល​បាន​រួមបញ្ចូល GenericName[lt]=Integruota programavimo aplinka GenericName[lv]=Integrēta izstrādes vide GenericName[nb]=Integrert utviklingsmiljø GenericName[nds]=Programmsmeed GenericName[nl]=Geïntegreerde ontwikkelomgeving GenericName[pl]=Zintegrowane środowisko programistyczne GenericName[pt]=Ambiente de Desenvolvimento Integrado GenericName[pt_BR]=Ambiente Integrado de Desenvolvimento GenericName[ru]=Интегрированная среда разработки GenericName[sk]=Integrované vývojové prostredie GenericName[sl]=Integrirano razvojno okolje GenericName[sv]=Integrerad utvecklingsmiljö GenericName[tr]=Bütünleşik Geliştirme Ortamı GenericName[ug]=يۈرۈشلەشتۈرۈلگەن ئىجادىيەت مۇھىتى GenericName[uk]=Комплексне середовище розробки GenericName[x-test]=xxIntegrated Development Environmentxx GenericName[zh_CN]=集成开发环境 GenericName[zh_TW]=整合開發環境 Categories=Qt;KDE;Development;IDE; NoDisplay=true diff --git a/plugins/grepview/grepdialog.cpp b/plugins/grepview/grepdialog.cpp index a3f470f2b5..dd8d501767 100644 --- a/plugins/grepview/grepdialog.cpp +++ b/plugins/grepview/grepdialog.cpp @@ -1,581 +1,585 @@ /*************************************************************************** * Copyright 1999-2001 Bernd Gehrmann and the KDevelop Team * * bernd@kdevelop.org * * Copyright 2007 Dukju Ahn * * Copyright 2010 Julien Desgats * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "grepdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grepviewplugin.h" #include "grepoutputview.h" #include "grepfindthread.h" #include "greputil.h" using namespace KDevelop; namespace { inline QString allOpenFilesString() { return i18n("All Open Files"); } inline QString allOpenProjectsString() { return i18n("All Open Projects"); } inline QStringList template_desc() { return QStringList{ QStringLiteral("verbatim"), QStringLiteral("word"), QStringLiteral("assignment"), QStringLiteral("->MEMBER("), QStringLiteral("class::MEMBER("), QStringLiteral("OBJECT->member("), }; } inline QStringList template_str() { return QStringList{ QStringLiteral("%s"), QStringLiteral("\\b%s\\b"), QStringLiteral("\\b%s\\b\\s*=[^=]"), QStringLiteral("\\->\\s*\\b%s\\b\\s*\\("), QStringLiteral("([a-z0-9_$]+)\\s*::\\s*\\b%s\\b\\s*\\("), QStringLiteral("\\b%s\\b\\s*\\->\\s*([a-z0-9_$]+)\\s*\\("), }; } inline QStringList repl_template() { return QStringList{ QStringLiteral("%s"), QStringLiteral("%s"), QStringLiteral("%s = "), QStringLiteral("->%s("), QStringLiteral("\\1::%s("), QStringLiteral("%s->\\1("), }; } inline QStringList filepatterns() { return QStringList{ QStringLiteral("*.h,*.hxx,*.hpp,*.hh,*.h++,*.H,*.tlh,*.cuh,*.cpp,*.cc,*.C,*.c++,*.cxx,*.ocl,*.inl,*.idl,*.c,*.cu,*.m,*.mm,*.M,*.y,*.ypp,*.yxx,*.y++,*.l,*.txt,*.xml,*.rc"), QStringLiteral("*.cpp,*.cc,*.C,*.c++,*.cxx,*.ocl,*.inl,*.c,*.cu,*.m,*.mm,*.M"), QStringLiteral("*.h,*.hxx,*.hpp,*.hh,*.h++,*.H,*.tlh,*.cuh,*.idl"), QStringLiteral("*.adb"), QStringLiteral("*.cs"), QStringLiteral("*.f"), QStringLiteral("*.html,*.htm"), QStringLiteral("*.hs"), QStringLiteral("*.java"), QStringLiteral("*.js"), QStringLiteral("*.php,*.php3,*.php4"), QStringLiteral("*.pl"), QStringLiteral("*.pp,*.pas"), QStringLiteral("*.py"), QStringLiteral("*.js,*.css,*.yml,*.rb,*.rhtml,*.html.erb,*.rjs,*.js.rjs,*.rxml,*.xml.builder"), QStringLiteral("CMakeLists.txt,*.cmake"), QStringLiteral("*"), }; } inline QStringList excludepatterns() { return QStringList{ QStringLiteral("/CVS/,/SCCS/,/.svn/,/_darcs/,/build/,/.git/"), QString(), }; } ///Separator used to separate search paths. inline QString pathsSeparator() { return (QStringLiteral(";")); } ///Returns the chosen directories or files (only the top directories, not subfiles) QList getDirectoryChoice(const QString& text) { QList ret; if (text == allOpenFilesString()) { const auto openDocuments = ICore::self()->documentController()->openDocuments(); ret.reserve(openDocuments.size()); for (auto* doc : openDocuments) { ret << doc->url(); } } else if (text == allOpenProjectsString()) { const auto projects = ICore::self()->projectController()->projects(); ret.reserve(projects.size()); for (auto* project : projects) { ret << project->path().toUrl(); } } else { const QStringList semicolonSeparatedFileList = text.split(pathsSeparator()); if (!semicolonSeparatedFileList.isEmpty() && QFileInfo::exists(semicolonSeparatedFileList[0])) { // We use QFileInfo to make sure this is really a semicolon-separated file list, not a file containing // a semicolon in the name. ret.reserve(semicolonSeparatedFileList.size()); for (const QString& file : semicolonSeparatedFileList) { ret << QUrl::fromLocalFile(file).adjusted(QUrl::StripTrailingSlash); } } else { ret << QUrl::fromUserInput(text).adjusted(QUrl::StripTrailingSlash); } } return ret; } ///Check if all directories are part of a project bool directoriesInProject(const QString& dir) { const auto urls = getDirectoryChoice(dir); return std::all_of(urls.begin(), urls.end(), [&](const QUrl& url) { IProject *proj = ICore::self()->projectController()->findProjectForUrl(url); return (proj && proj->path().toUrl().isLocalFile()); }); } ///Max number of items in paths combo box. const int pathsMaxCount = 25; } GrepDialog::GrepDialog(GrepViewPlugin *plugin, QWidget *parent, bool show) : QDialog(parent), Ui::GrepWidget(), m_plugin(plugin), m_show(show) { setAttribute(Qt::WA_DeleteOnClose); // if we don't intend on showing the dialog, we can skip all UI setup if (!m_show) { return; } setWindowTitle( i18n("Find/Replace in Files") ); setupUi(this); adjustSize(); auto searchButton = buttonBox->button(QDialogButtonBox::Ok); Q_ASSERT(searchButton); searchButton->setText(i18nc("@action:button", "Search...")); searchButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); connect(searchButton, &QPushButton::clicked, this, &GrepDialog::startSearch); connect(buttonBox, &QDialogButtonBox::rejected, this, &GrepDialog::reject); KConfigGroup cg = ICore::self()->activeSession()->config()->group( "GrepDialog" ); patternCombo->addItems( cg.readEntry("LastSearchItems", QStringList()) ); patternCombo->setInsertPolicy(QComboBox::InsertAtTop); patternCombo->setCompleter(nullptr); templateTypeCombo->addItems(template_desc()); templateTypeCombo->setCurrentIndex( cg.readEntry("LastUsedTemplateIndex", 0) ); templateEdit->addItems( cg.readEntry("LastUsedTemplateString", template_str()) ); templateEdit->setEditable(true); templateEdit->setCompletionMode(KCompletion::CompletionPopup); KCompletion* comp = templateEdit->completionObject(); connect(templateEdit, QOverload::of(&KComboBox::returnPressed), comp, QOverload::of(&KCompletion::addItem)); for(int i=0; icount(); i++) comp->addItem(templateEdit->itemText(i)); replacementTemplateEdit->addItems( cg.readEntry("LastUsedReplacementTemplateString", repl_template()) ); replacementTemplateEdit->setEditable(true); replacementTemplateEdit->setCompletionMode(KCompletion::CompletionPopup); comp = replacementTemplateEdit->completionObject(); connect(replacementTemplateEdit, QOverload::of(&KComboBox::returnPressed), comp, QOverload::of(&KCompletion::addItem)); for(int i=0; icount(); i++) comp->addItem(replacementTemplateEdit->itemText(i)); regexCheck->setChecked(cg.readEntry("regexp", false )); caseSensitiveCheck->setChecked(cg.readEntry("case_sens", true)); searchPaths->setCompletionObject(new KUrlCompletion()); searchPaths->setAutoDeleteCompletionObject(true); QList projects = m_plugin->core()->projectController()->projects(); searchPaths->addItems(cg.readEntry("SearchPaths", QStringList(!projects.isEmpty() ? allOpenProjectsString() : QDir::homePath() ) )); searchPaths->setInsertPolicy(QComboBox::InsertAtTop); syncButton->setIcon(QIcon::fromTheme(QStringLiteral("dirsync"))); syncButton->setMenu(createSyncButtonMenu()); depthSpin->setValue(cg.readEntry("depth", -1)); limitToProjectCheck->setChecked(cg.readEntry("search_project_files", true)); filesCombo->addItems(cg.readEntry("file_patterns", filepatterns())); excludeCombo->addItems(cg.readEntry("exclude_patterns", excludepatterns()) ); connect(templateTypeCombo, QOverload::of(&KComboBox::activated), this, &GrepDialog::templateTypeComboActivated); connect(patternCombo, &QComboBox::editTextChanged, this, &GrepDialog::patternComboEditTextChanged); patternComboEditTextChanged( patternCombo->currentText() ); patternCombo->setFocus(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect(searchPaths, &KComboBox::textActivated, +#else connect(searchPaths, QOverload::of(&KComboBox::activated), +#endif this, &GrepDialog::setSearchLocations); directorySelector->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); connect(directorySelector, &QPushButton::clicked, this, &GrepDialog::selectDirectoryDialog ); } void GrepDialog::selectDirectoryDialog() { const QString dirName = QFileDialog::getExistingDirectory( this, i18nc("@title:window", "Select directory to search in"), searchPaths->lineEdit()->text()); if (!dirName.isEmpty()) { setSearchLocations(dirName); } } void GrepDialog::addUrlToMenu(QMenu* menu, const QUrl& url) { QAction* action = menu->addAction(m_plugin->core()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain)); action->setData(QVariant(url.toString(QUrl::PreferLocalFile))); connect(action, &QAction::triggered, this, &GrepDialog::synchronizeDirActionTriggered); } void GrepDialog::addStringToMenu(QMenu* menu, const QString& string) { QAction* action = menu->addAction(string); action->setData(QVariant(string)); connect(action, &QAction::triggered, this, &GrepDialog::synchronizeDirActionTriggered); } void GrepDialog::synchronizeDirActionTriggered(bool) { auto* action = qobject_cast(sender()); Q_ASSERT(action); setSearchLocations(action->data().toString()); } QMenu* GrepDialog::createSyncButtonMenu() { auto* ret = new QMenu(this); QSet hadUrls; IDocument *doc = m_plugin->core()->documentController()->activeDocument(); if ( doc ) { Path url = Path(doc->url()).parent(); // always add the current file's parent directory hadUrls.insert(url); addUrlToMenu(ret, url.toUrl()); url = url.parent(); while(m_plugin->core()->projectController()->findProjectForUrl(url.toUrl())) { if(hadUrls.contains(url)) break; hadUrls.insert(url); addUrlToMenu(ret, url.toUrl()); url = url.parent(); } } QVector otherProjectUrls; const auto projects = m_plugin->core()->projectController()->projects(); for (IProject* project : projects) { if (!hadUrls.contains(project->path())) { otherProjectUrls.append(project->path().toUrl()); } } // sort the remaining project URLs alphabetically std::sort(otherProjectUrls.begin(), otherProjectUrls.end()); for (const QUrl& url : qAsConst(otherProjectUrls)) { addUrlToMenu(ret, url); } ret->addSeparator(); addStringToMenu(ret, allOpenFilesString()); addStringToMenu(ret, allOpenProjectsString()); return ret; } GrepDialog::~GrepDialog() { } void GrepDialog::setVisible(bool visible) { QDialog::setVisible(visible && m_show); } void GrepDialog::closeEvent(QCloseEvent* closeEvent) { Q_UNUSED(closeEvent); if (!m_show) { return; } KConfigGroup cg = ICore::self()->activeSession()->config()->group( "GrepDialog" ); // memorize the last patterns and paths cg.writeEntry("LastSearchItems", qCombo2StringList(patternCombo)); cg.writeEntry("regexp", regexCheck->isChecked()); cg.writeEntry("depth", depthSpin->value()); cg.writeEntry("search_project_files", limitToProjectCheck->isChecked()); cg.writeEntry("case_sens", caseSensitiveCheck->isChecked()); cg.writeEntry("exclude_patterns", qCombo2StringList(excludeCombo)); cg.writeEntry("file_patterns", qCombo2StringList(filesCombo)); cg.writeEntry("LastUsedTemplateIndex", templateTypeCombo->currentIndex()); cg.writeEntry("LastUsedTemplateString", qCombo2StringList(templateEdit)); cg.writeEntry("LastUsedReplacementTemplateString", qCombo2StringList(replacementTemplateEdit)); cg.writeEntry("SearchPaths", qCombo2StringList(searchPaths)); cg.sync(); } void GrepDialog::templateTypeComboActivated(int index) { templateEdit->setCurrentItem( template_str().at(index), true ); replacementTemplateEdit->setCurrentItem( repl_template().at(index), true ); } void GrepDialog::setSettings(const GrepJobSettings& settings) { patternCombo->setEditText(settings.pattern); patternComboEditTextChanged(settings.pattern); m_settings.pattern = settings.pattern; limitToProjectCheck->setEnabled(settings.projectFilesOnly); limitToProjectLabel->setEnabled(settings.projectFilesOnly); m_settings.projectFilesOnly = settings.projectFilesOnly; // Note: everything else is set by a user } GrepJobSettings GrepDialog::settings() const { return m_settings; } void GrepDialog::historySearch(QVector &settingsHistory) { // clear the current settings history and pass it to a job list m_historyJobSettings.clear(); m_historyJobSettings.swap(settingsHistory); // check if anything is do be done and if all projects are loaded if (!m_historyJobSettings.empty() && !checkProjectsOpened()) { connect(KDevelop::ICore::self()->projectController(), &KDevelop::IProjectController::projectOpened, this, &GrepDialog::checkProjectsOpened); } } void GrepDialog::setSearchLocations(const QString& dir) { if (!dir.isEmpty()) { if (m_show) { if (QDir::isAbsolutePath(dir)) { static_cast(searchPaths->completionObject())->setDir( QUrl::fromLocalFile(dir) ); } if (searchPaths->contains(dir)) { searchPaths->removeItem(searchPaths->findText(dir)); } searchPaths->insertItem(0, dir); searchPaths->setCurrentItem(dir); if (searchPaths->count() > pathsMaxCount) { searchPaths->removeItem(searchPaths->count() - 1); } } else { m_settings.searchPaths = dir; } } m_settings.projectFilesOnly = directoriesInProject(dir); } void GrepDialog::patternComboEditTextChanged( const QString& text) { buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); } bool GrepDialog::checkProjectsOpened() { // only proceed if all projects have been opened if (KDevelop::ICore::self()->activeSession()->config()->group("General Options").readEntry("Open Projects", QList()).count() != KDevelop::ICore::self()->projectController()->projects().count()) return false; const auto projects = KDevelop::ICore::self()->projectController()->projects(); for (IProject* p : projects) { if (!p->isReady()) return false; } // do the grep jobs one by one connect(m_plugin, &GrepViewPlugin::grepJobFinished, this, &GrepDialog::nextHistory); QTimer::singleShot(0, this, [=]() {nextHistory(true);}); return true; } void GrepDialog::nextHistory(bool next) { if (next && !m_historyJobSettings.empty()) { m_settings = m_historyJobSettings.takeFirst(); startSearch(); } else { close(); } } bool GrepDialog::isPartOfChoice(const QUrl& url) const { const auto choices = getDirectoryChoice(m_settings.searchPaths); for (const QUrl& choice : choices) { if(choice.isParentOf(url) || choice == url) return true; } return false; } void GrepDialog::startSearch() { // if m_show is false, all settings are fixed in m_settings if (m_show) updateSettings(); const QStringList include = GrepFindFilesThread::parseInclude(m_settings.files); const QStringList exclude = GrepFindFilesThread::parseExclude(m_settings.exclude); // search for unsaved documents QList unsavedFiles; const auto documents = ICore::self()->documentController()->openDocuments(); for (IDocument* doc : documents) { QUrl docUrl = doc->url(); if (doc->state() != IDocument::Clean && isPartOfChoice(docUrl) && QDir::match(include, docUrl.fileName()) && !QDir::match(exclude, docUrl.toLocalFile()) ) { unsavedFiles << doc; } } if(!ICore::self()->documentController()->saveSomeDocuments(unsavedFiles)) { close(); return; } const QString descriptionOrUrl(m_settings.searchPaths); QList choice = getDirectoryChoice(descriptionOrUrl); QString description = descriptionOrUrl; // Shorten the description if(descriptionOrUrl != allOpenFilesString() && descriptionOrUrl != allOpenProjectsString()) { auto prettyFileName = [](const QUrl& url) { return ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain); }; if (choice.size() > 1) { description = i18np("%2, and %1 more item", "%2, and %1 more items", choice.size() - 1, prettyFileName(choice[0])); } else if (!choice.isEmpty()) { description = prettyFileName(choice[0]); } } GrepOutputView *toolView = (GrepOutputView*)ICore::self()->uiController()->findToolView( i18n("Find/Replace in Files"), m_plugin->toolViewFactory(), m_settings.fromHistory ? IUiController::Create : IUiController::CreateAndRaise); if (m_settings.fromHistory) { // when restored from history, only display the parameters toolView->renewModel(m_settings, i18n("Search \"%1\" in %2", m_settings.pattern, description)); emit m_plugin->grepJobFinished(true); } else { GrepOutputModel* outputModel = toolView->renewModel(m_settings, i18n("Search \"%1\" in %2 (at time %3)", m_settings.pattern, description, QTime::currentTime().toString(QStringLiteral("hh:mm")))); GrepJob* job = m_plugin->newGrepJob(); connect(job, &GrepJob::showErrorMessage, toolView, &GrepOutputView::showErrorMessage); //the GrepOutputModel gets the 'showMessage' signal to store it and forward //it to toolView connect(job, &GrepJob::showMessage, outputModel, &GrepOutputModel::showMessageSlot); connect(outputModel, &GrepOutputModel::showMessage, toolView, &GrepOutputView::showMessage); connect(toolView, &GrepOutputView::outputViewIsClosed, job, [=]() {job->kill();}); job->setOutputModel(outputModel); job->setDirectoryChoice(choice); job->setSettings(m_settings); ICore::self()->runController()->registerJob(job); } m_plugin->rememberSearchDirectory(descriptionOrUrl); // if m_show is false, the dialog is closed somewhere else if (m_show) close(); } void GrepDialog::updateSettings() { if (limitToProjectCheck->isEnabled()) m_settings.projectFilesOnly = limitToProjectCheck->isChecked(); m_settings.caseSensitive = caseSensitiveCheck->isChecked(); m_settings.regexp = regexCheck->isChecked(); m_settings.depth = depthSpin->value(); m_settings.pattern = patternCombo->currentText(); m_settings.searchTemplate = templateEdit->currentText().isEmpty() ? QStringLiteral("%s") : templateEdit->currentText(); m_settings.replacementTemplate = replacementTemplateEdit->currentText(); m_settings.files = filesCombo->currentText(); m_settings.exclude = excludeCombo->currentText(); m_settings.searchPaths = searchPaths->currentText(); } diff --git a/plugins/grepview/grepfindthread.cpp b/plugins/grepview/grepfindthread.cpp index 714512d3de..74d04cc2ba 100644 --- a/plugins/grepview/grepfindthread.cpp +++ b/plugins/grepview/grepfindthread.cpp @@ -1,176 +1,178 @@ #include "grepfindthread.h" #include "debug.h" #include #include #include #include #include #include #include +#include using KDevelop::IndexedString; /** * @return Return true in case @p url is in @p dir within a maximum depth of @p maxDepth */ static bool isInDirectory(const QUrl& url, const QUrl& dir, int maxDepth) { QUrl folderUrl = url.adjusted(QUrl::RemoveFilename); int currentLevel = maxDepth; while(currentLevel > 0) { folderUrl = folderUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); if ( folderUrl == dir.adjusted(QUrl::StripTrailingSlash) ) { return true; } currentLevel--; } return false; } // the abort parameter must be volatile so that it // is evaluated every time - optimization might prevent that static QList thread_getProjectFiles(const QUrl& dir, int depth, const QStringList& include, const QStringList& exlude, volatile bool &abort) { ///@todo This is not thread-safe! KDevelop::IProject *project = KDevelop::ICore::self()->projectController()->findProjectForUrl( dir ); QList res; if(!project) return res; const QSet fileSet = project->fileSet(); for (const IndexedString& item : fileSet) { if(abort) break; QUrl url = item.toUrl(); if( url != dir ) { if ( depth == 0 ) { if ( url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) != dir.adjusted(QUrl::StripTrailingSlash) ) { continue; } } else if ( !dir.isParentOf(url) ) { continue; } else if ( depth > 0 ) { // To ensure the current file is within the defined depth limit, navigate up the tree for as many levels // as the depth value, trying to find "dir", which is the project folder. If after all the loops there // is no match, it means the current file is deeper down the project tree than the limit depth, and so // it must be skipped. if(!isInDirectory(url, dir, depth)) continue; } } if( QDir::match(include, url.fileName()) && !QDir::match(exlude, url.toLocalFile()) ) res << url; } return res; } static QList thread_findFiles(const QDir& dir, int depth, const QStringList& include, const QStringList& exclude, volatile bool &abort) { QFileInfoList infos = dir.entryInfoList(include, QDir::NoDotAndDotDot|QDir::Files|QDir::Readable); if(!QFileInfo(dir.path()).isDir()) infos << QFileInfo(dir.path()); QList dirFiles; for (const QFileInfo& currFile : qAsConst(infos)) { QString currName = currFile.canonicalFilePath(); if(!QDir::match(exclude, currName)) dirFiles << QUrl::fromLocalFile(currName); } if(depth != 0) { static const QDir::Filters dirFilter = QDir::NoDotAndDotDot|QDir::AllDirs|QDir::Readable|QDir::NoSymLinks; const auto dirs = dir.entryInfoList(QStringList(), dirFilter); for (const QFileInfo& currDir : dirs) { if(abort) break; QString canonical = currDir.canonicalFilePath(); if (!canonical.startsWith(dir.canonicalPath())) continue; if ( depth > 0 ) { depth--; } dirFiles << thread_findFiles(canonical, depth, include, exclude, abort); } } return dirFiles; } GrepFindFilesThread::GrepFindFilesThread(QObject* parent, const QList& startDirs, int depth, const QString& pats, const QString& excl, bool onlyProject) : QThread(parent) , m_startDirs(startDirs) , m_patString(pats) , m_exclString(excl) , m_depth(depth) , m_project(onlyProject) , m_tryAbort(false) { setTerminationEnabled(false); } void GrepFindFilesThread::tryAbort() { m_tryAbort = true; } bool GrepFindFilesThread::triesToAbort() const { return m_tryAbort; } void GrepFindFilesThread::run() { QStringList include = GrepFindFilesThread::parseInclude(m_patString); QStringList exclude = GrepFindFilesThread::parseExclude(m_exclString); qCDebug(PLUGIN_GREPVIEW) << "running with start dir" << m_startDirs; for (const QUrl& directory : qAsConst(m_startDirs)) { if(m_project) m_files += thread_getProjectFiles(directory, m_depth, include, exclude, m_tryAbort); else { m_files += thread_findFiles(directory.toLocalFile(), m_depth, include, exclude, m_tryAbort); } } } QList GrepFindFilesThread::files() const { - auto tmpList = QList::fromSet(m_files.toSet()); + auto tmpList = m_files; std::sort(tmpList.begin(), tmpList.end()); + tmpList.erase(std::unique(tmpList.begin(), tmpList.end()), tmpList.end()); return tmpList; } QStringList GrepFindFilesThread::parseExclude(const QString& excl) { QStringList exclude; // Split around commas or spaces const auto excludesList = excl.split(QRegExp(QStringLiteral(",|\\s")), QString::SkipEmptyParts); exclude.reserve(excludesList.size()); for (const auto& sub : excludesList) { exclude << QStringLiteral("*%1*").arg(sub); } return exclude; } QStringList GrepFindFilesThread::parseInclude(const QString& inc) { // Split around commas or spaces return inc.split(QRegExp(QStringLiteral(",|\\s")), QString::SkipEmptyParts); } diff --git a/plugins/grepview/kdevgrepview.json b/plugins/grepview/kdevgrepview.json index fe10557347..bf6303efbe 100644 --- a/plugins/grepview/kdevgrepview.json +++ b/plugins/grepview/kdevgrepview.json @@ -1,71 +1,58 @@ { "KPlugin": { "Category": "Utilities", "Description": "Allows fast searching of multiple files using patterns or regular expressions. And allow to replace it too.", - "Description[ar]": "تسمح بالبحث السريع في ملفّات متعدّدة باستخدام أنماط أو تعابير نمطيّة. وتسمح بالاستبدال أيضًا.", "Description[ca@valencia]": "Permet la cerca ràpida de múltiples fitxers usant patrons o expressions regulars. I també permet substitucions.", "Description[ca]": "Permet la cerca ràpida de múltiples fitxers usant patrons o expressions regulars. I també permet substitucions.", "Description[cs]": "Umožní rychlé vyhledávání více souborů za použití řetězců nebo regulárních výrazů. Umožní také jejich nahrazování.", "Description[de]": "Ermöglicht es, Dateien mit Hilfe von Mustern und regulären Ausdrücken zu durchsuchen bzw. Ersetzungen vorzunehmen.", - "Description[el]": "Επιτρέπει γρήγορη αναζήτηση πολλών αρχείων με χρήση προτύπων ή κανονικών εκφράσεων. Επιτρέπει και την αντικατάστασή τους επίσης.", "Description[en_GB]": "Allows fast searching of multiple files using patterns or regular expressions. And allow to replace it too.", "Description[es]": "Permite la búsqueda rápida en múltiples archivos usando patrones o expresiones regulares. También permite realizar sustituciones.", "Description[et]": "Lubab mustreid või regulaaravaldisi kasutades kiiresti paljudes failides teksti otsida, samuti asendada.", "Description[fr]": "Permet de rechercher rapidement dans plusieurs fichiers en utilisant des motifs ou des expressions rationnelles. Et permet de le remplacer aussi.", "Description[gl]": "Permite facer unha busca rápida en varios ficheiros empregando padróns ou expresións regulares, e tamén realizar substitucións.", "Description[it]": "Consente la ricerca veloce di file multipli usando espressioni regolari o modelli. E consente anche di sostituirli.", "Description[nl]": "Staat toe snel te zoeken naar meerdere bestanden met gebruik van patronen of reguliere expressies. Staat ook vervanging toe.", "Description[pl]": "Szybko znajduje wiele plików wykorzystując wzorce lub regularne wyrażenia. Umożliwia także ich zastępowanie.", "Description[pt]": "Permite a pesquisa rápida em vários ficheiros, usando padrões ou expressões regulares. Permite também a sua substituição.", "Description[pt_BR]": "Permite pesquisar rapidamente em vários arquivos, usando padrões ou expressões regulares e também realizar substituições.", "Description[sk]": "Umožní rýchle hľadanie viacerých súborov pomocou vzorov alebo regulárnych výrazov a umožní ich nahradiť.", "Description[sl]": "Omogoča hitro iskanje po več datotekah z uporabo vzorcev ali regularnih izrazov. Omogoča tudi zamenjave.", "Description[sv]": "Tillåter snabb sökning i flera filer med mönster eller reguljära uttryck, och tillåter dessutom ersättning.", "Description[tr]": "Kalıplar veya düzenli ifadeler kullanarak birden çok dosyada hızlı aramaya izin verir. Yer değiştirmeye de izin verir.", "Description[uk]": "Надає можливості швидкого пошуку та заміни у декількох файлів на основі шаблонів або формальних виразів.", "Description[x-test]": "xxAllows fast searching of multiple files using patterns or regular expressions. And allow to replace it too.xx", "Description[zh_CN]": "可以用模式或正则表达式快速搜索多个文件,还可以替换。", - "Description[zh_TW]": "允許使用樣式或正規表示式來快速搜尋與取代多個檔案。", "Icon": "kfind", "Id": "kdevgrepview", "Name": "Find/Replace In Files", - "Name[ar]": "اعثر واستبدل في الملفّات", - "Name[bg]": "Търсене и заместване във файлове", - "Name[bs]": "Nađi/zamijeni u datotekama", "Name[ca@valencia]": "Cerca i substitució en fitxers", "Name[ca]": "Cerca i substitució en fitxers", "Name[cs]": "Hledat-nahradit v souborech", - "Name[da]": "Find/erstat i filer", "Name[de]": "In Dateien suchen/ersetzen", - "Name[el]": "Αναζήτηση/Αντικατάσταση σε αρχεία", "Name[en_GB]": "Find/Replace In Files", "Name[es]": "Buscar/sustituir en archivos", "Name[et]": "Failides otsimine ja asendamine", "Name[fr]": "Chercher / Remplacer dans les fichiers", "Name[gl]": "Atopar e substituír en ficheiros", - "Name[hu]": "Keresés/csere fájlokban", "Name[it]": "Cerca/Sostituisci nei file", - "Name[kk]": "Файлдарда іздеу/ауыстыру", "Name[nb]": "Finn/erstatt i filer", - "Name[nds]": "Söken un Utwesseln in Dateien", "Name[nl]": "Zoeken/vervangen in bestanden", "Name[pl]": "Znajdź/zastąp w plikach", "Name[pt]": "Procurar/Substituir nos Ficheiros", "Name[pt_BR]": "Procurar/substituir nos arquivos", "Name[ru]": "Поиск/замена в файлах", "Name[sk]": "Hľadať/nahradiť v súboroch", "Name[sl]": "Najdi/zamenjaj v datotekah", "Name[sv]": "Sök eller ersätt i filer", "Name[tr]": "Bu Dosyalarda Bul ve Değiştir", - "Name[ug]": "ھۆججەت ئىچىدىن ئىزدەش/ئالماشتۇرۇش", "Name[uk]": "Пошук або заміна у файлах", "Name[x-test]": "xxFind/Replace In Filesxx", "Name[zh_CN]": "在文件中查找/替换", - "Name[zh_TW]": "在檔案中尋找/取代", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/heaptrack/kdevheaptrack.json b/plugins/heaptrack/kdevheaptrack.json index 20b6fb7b01..158a30619d 100644 --- a/plugins/heaptrack/kdevheaptrack.json +++ b/plugins/heaptrack/kdevheaptrack.json @@ -1,91 +1,86 @@ { "KPlugin": { "Authors": [ { "Name": "Anton Anikin", "Name[ca@valencia]": "Anton Anikin", "Name[ca]": "Anton Anikin", "Name[cs]": "Anton Anikin", "Name[de]": "Anton Anikin", - "Name[el]": "Anton Anikin", "Name[en_GB]": "Anton Anikin", "Name[es]": "Anton Anikin", "Name[et]": "Anton Anikin", "Name[fr]": "Anton Anikin", "Name[gl]": "Anton Anikin", "Name[it]": "Anton Anikin", "Name[nl]": "Anton Anikin", "Name[nn]": "Anton Anikin", "Name[pl]": "Anton Anikin", "Name[pt]": "Anton Anikin", "Name[pt_BR]": "Anton Anikin", "Name[ru]": "Антон Аникин", "Name[sk]": "Anton Anikin", "Name[sl]": "Anton Anikin", "Name[sv]": "Anton Anikin", "Name[tr]": "Anton Anikin", "Name[uk]": "Anton Anikin", "Name[x-test]": "xxAnton Anikinxx", "Name[zh_CN]": "Anton Anikin" } ], "Category": "Analyzers", "Description": "This plugin integrates Heaptrack to KDevelop", "Description[ca@valencia]": "Aquest connector integra el Heaptrack al KDevelop", "Description[ca]": "Aquest connector integra el Heaptrack al KDevelop", "Description[cs]": "Tento modul integruje Heaptrack do KDevelop", "Description[de]": "Dieses Modul integriert Heaptrack in KDevelop", - "Description[el]": "Αυτό το πρόσθετο ενσωματώνει το Heaptrack στο KDevelop", "Description[en_GB]": "This plugin integrates Heaptrack to KDevelop", "Description[es]": "Este complemento integra Heaptrack en KDevelop", - "Description[et]": "See plugin lõimib Heaptracki KDevelopiga", "Description[fr]": "Ce module externe intègre Heaptrack à KDevelop", "Description[gl]": "Este complemento integra Heaptrack con KDevelop.", "Description[it]": "Questa estensione integra Heaptrack in KDevelop", "Description[nl]": "Deze plugin integreert Heaptrack in KDevelop", "Description[pl]": "Wplata Heaptrack w KDevelop", "Description[pt]": "Este 'plugin' integra o Heaptrack no KDevelop", "Description[pt_BR]": "Este plugin integra o Heaptrack ao KDevelop", "Description[sk]": "Tento plugin integruje Heaptrack pre KDevelop", "Description[sl]": "Ta vstavek v KDevelop vgradi Heaptrack", "Description[sv]": "Insticksprogrammet integrerar Heaptrack i KDevelop", "Description[tr]": "Bu eklenti Heaptrack uygulamasını KDevelop ile bütünleştirir", "Description[uk]": "За допомогою цього додатка можна інтегрувати Heaptrack до KDevelop", "Description[x-test]": "xxThis plugin integrates Heaptrack to KDevelopxx", "Description[zh_CN]": "此插件将 Heaptrack 整合到 KDevelop", "Icon": "office-chart-area", "Id": "kdevheaptrack", "License": "GPL", "Name": "Heaptrack Support", "Name[ca@valencia]": "Implementació del Heaptrack", "Name[ca]": "Implementació del Heaptrack", "Name[cs]": "Podpora Heaptracku", "Name[de]": "Heaptrack-Unterstützung", - "Name[el]": "Υποστήριξη heaptrack", "Name[en_GB]": "Heaptrack Support", "Name[es]": "Implementación de Heaptrack", - "Name[et]": "Heaptracki toetus", "Name[fr]": "Prise en charge Heaptrack", "Name[gl]": "Compatibilidade con Heaptrack.", "Name[it]": "Supporto per Heaptrack", "Name[nl]": "Ondersteuning van Heaptrack", "Name[pl]": "Obsługa Heaptrack", "Name[pt]": "Suporte para o Heaptrack", "Name[pt_BR]": "Suporte ao Heaptrack", "Name[sk]": "Podpora Heaptrack", "Name[sl]": "Podpora za Heaptrack", "Name[sv]": "Heaptrack-stöd", "Name[tr]": "Heaptrack Desteği", "Name[uk]": "Підтримка Heaptrack", "Name[x-test]": "xxHeaptrack Supportxx", "Name[zh_CN]": "Heaptrack 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IExecutePlugin@kdevexecute" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/kdeprovider/kdevkdeprovider.json b/plugins/kdeprovider/kdevkdeprovider.json index 7f435ff405..e0b63267a9 100644 --- a/plugins/kdeprovider/kdevkdeprovider.json +++ b/plugins/kdeprovider/kdevkdeprovider.json @@ -1,98 +1,93 @@ { "KPlugin": { "Authors": [ { "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Utilities", "Description": "This plugin helps to obtain KDE projects", - "Description[bs]": "Ovaj dodatak omogućava dobijanje KDE projekada", "Description[ca@valencia]": "Aquest connector ajuda a obtindre els projectes KDE", "Description[ca]": "Aquest connector ajuda a obtenir els projectes KDE", "Description[de]": "Dieses Modul hilft dabei KDE-Projekte zu beziehen", - "Description[el]": "Αυτό το πρόσθετο βοηθάει στην ανάκτηση KDE έργων", "Description[en_GB]": "This plugin helps to obtain KDE projects", "Description[es]": "Este complemento le ayuda a obtener proyectos de KDE", "Description[et]": "See plugin aitab hankida KDE projekte", "Description[fi]": "Tämä liitännäinen auttaa KDE-projektien hakemisessa", "Description[fr]": "Ce module aide à obtenir des projets KDE", "Description[gl]": "Este complemento permite obter proxectos de KDE.", "Description[it]": "Questa estensione consente di ottenere progetti di KDE", "Description[nl]": "Deze plugin helpt om KDE-projects te verkrijgen", "Description[pl]": "Pobiera projekty KDE", "Description[pt]": "Este 'plugin' ajuda a obter projectos do KDE", "Description[pt_BR]": "Este plugin ajuda a obter projetos do KDE", "Description[sk]": "Tento modul pomáha získavať KDE projekty", "Description[sl]": "Ta vstavek pomaga pri pridobivanju KDE-jevih projektov", "Description[sv]": "Insticksprogrammet hjälper till att erhålla KDE-projekt", "Description[tr]": "Bu eklenti KDE projelerini edinmeye yardım eder", "Description[uk]": "За допомогою цього додатка можна отримувати проєкти KDE", "Description[x-test]": "xxThis plugin helps to obtain KDE projectsxx", "Description[zh_CN]": "此插件帮助您获取 KDE 工程", "Icon": "kde", "Id": "kdevkdeprovider", "License": "GPL", "Name": "KDE Provider", - "Name[bs]": "KDE pružalac", "Name[ca@valencia]": "Proveïdor del KDE", "Name[ca]": "Proveïdor del KDE", "Name[cs]": "Poskytovatel KDE", "Name[de]": "KDE-Provider", - "Name[el]": "Πάροχος KDE", "Name[en_GB]": "KDE Provider", "Name[es]": "Proveedor de KDE", "Name[et]": "KDE pakkuja", "Name[fi]": "KDE-tarjoaja", "Name[fr]": "Fournisseur KDE", "Name[gl]": "Provedor de KDE", "Name[it]": "Fornitore di KDE", "Name[nl]": "KDE-leverancier", "Name[pl]": "Dostawca KDE", "Name[pt]": "Fornecedor do KDE", "Name[pt_BR]": "Fornecedor do KDE", "Name[sk]": "Poskytovateľ KDE", "Name[sl]": "KDE-jev ponudnik", "Name[sv]": "KDE-leverantör", "Name[tr]": "KDE Sağlayıcı", "Name[uk]": "Надавач даних KDE", "Name[x-test]": "xxKDE Providerxx", "Name[zh_CN]": "KDE 提供器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IBasicVersionControl@kdevgit" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectProvider" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/konsole/kdevkonsoleview.json b/plugins/konsole/kdevkonsoleview.json index 90981e9c6a..3aba984337 100644 --- a/plugins/konsole/kdevkonsoleview.json +++ b/plugins/konsole/kdevkonsoleview.json @@ -1,71 +1,57 @@ { "KPlugin": { "Category": "Utilities", "Description": "This plugin provides KDevelop with an embedded konsole for quick and easy command line access.", - "Description[ar]": "توفّر هذه الملحقة لمطوّرك كونسول مضمّن لوصول سريع وسهل لسطر الأوامر", "Description[ca@valencia]": "Aquest connector proporciona al KDevelop un Konsole incrustat per accedir de manera ràpida i fàcil a la línia d'ordres.", "Description[ca]": "Aquest connector proporciona al KDevelop un Konsole incrustat per accedir de manera ràpida i fàcil a la línia d'ordres.", "Description[cs]": "Tento zásuvný modul poskytuje vestavěnou konzoli KDevelopu pro rychlý a jednoduchý přístup do příkazové řádky.", "Description[de]": "Dieses Modul stattet KDevelop mit einer eingebetteten Konsole zum einfachen Zugriff auf die Befehlszeile aus.", - "Description[el]": "Αυτό το πρόσθετο προσφέρει στο KDevelop μια ενσωματωμένη konsole για γρήγορη και εύκολη πρόσβαση σε γραμμή εντολών.", "Description[en_GB]": "This plugin provides KDevelop with an embedded konsole for quick and easy command line access.", "Description[es]": "Este complemento proporciona una consola integrada a KDevelop para acceder a la línea de órdenes de forma rápida y fácil.", "Description[et]": "See plugin pakub KDevelopile põimitud konsooli käsurea kiireks ja lihtsaks kasutamiseks.", "Description[fr]": "Ce module apporte à KDevelop une konsole intégrée pour un accès à la ligne de commande rapide et facile.", "Description[gl]": "Este complemento fornécelle a KDevelop un konsole integrado para dispor de acceso rápido e sinxelo á liña de ordes.", "Description[it]": "Questa estensione dota KDevelop di una console integrata per un rapido e semplice accesso alla riga di comando.", "Description[nl]": "Deze plugin biedt KDevelop een ingebed konsole voor snelle en gemakkelijke toegang tot commandoregels.", "Description[pl]": "Dodaje osadzoną konsolę do szybkiego i łatwego dostępu do wiersza poleceń w KDevelop.", "Description[pt]": "Este 'plugin' oferece ao KDevelop um Konsole incorporado para aceder rápida e facilmente à linha de comandos.", "Description[pt_BR]": "Este plugin fornece ao KDevelop um terminal embutido para acesso rápido e fácil à linha de comando.", "Description[sk]": "Tento plugin poskytuje KDevelop so zabudovanou konzolou na rýchly a ľahký prístup k príkazovému riadku.", "Description[sl]": "Vstavek v KDevelop vgradi program Konsole za hiter in preprost dostop do ukazne vrstice.", "Description[sv]": "Insticksprogrammet ger KDevelop en inbyggd terminal för snabb och enkel åtkomst av kommandoraden.", "Description[tr]": "Bu eklenti hızlı ve kolay komut satırı erişimi için KDevelop'a gömülü bir uçbirim sağlar.", "Description[uk]": "За допомогою цього додатка у KDevelop можна буде скористатися вбудованою konsole, яка пришвидшить і полегшить доступ до командного рядка.", "Description[x-test]": "xxThis plugin provides KDevelop with an embedded konsole for quick and easy command line access.xx", "Description[zh_CN]": "此插件为 KDevelop 提供了一个快速方便地访问命令行的嵌入式控制台。", - "Description[zh_TW]": "此外掛程式提供 KDevelop 一個嵌入式的 konsole,能快速地使用命令列。", "Icon": "utilities-terminal", "Id": "kdevkonsoleview", "Name": "Konsole Integration", - "Name[ar]": "تكامل كونسول", - "Name[bg]": "Интегриране на конзола", - "Name[bs]": "Integracija konzole", "Name[ca@valencia]": "Integració del Konsole", "Name[ca]": "Integració del Konsole", "Name[cs]": "Integrace Konsole", - "Name[da]": "Integration af Konsole", "Name[de]": "Konsole-Integration", - "Name[el]": "Ενσωμάτωση Konsole", "Name[en_GB]": "Konsole Integration", "Name[es]": "Integración de Konsole", - "Name[et]": "Lõimimine Konsooliga", "Name[fr]": "Intégration de Konsole", "Name[gl]": "Integración con Konsole", - "Name[hu]": "Konsole integráció", "Name[it]": "Integrazione di Konsole", - "Name[kk]": "Konsole біріктіруі", "Name[nb]": "Konsole-integrering", - "Name[nds]": "Konsool-Inbinnen", "Name[nl]": "Console-integratie", "Name[pl]": "Wplecenie Konsoli", "Name[pt]": "Integração com o Konsole", "Name[pt_BR]": "Integração com o Konsole", "Name[ru]": "Интеграция Konsole", "Name[sk]": "Integrácia Konsole", "Name[sl]": "Vgradnja Konsole", "Name[sv]": "Integrering av Konsole", "Name[tr]": "Konsole Bütünleşmesi", - "Name[ug]": "Konsole يۈرۈشلەشتۈرۈلۈشى", "Name[uk]": "Інтеграція з Konsole", "Name[x-test]": "xxKonsole Integrationxx", "Name[zh_CN]": "Konsole 整合", - "Name[zh_TW]": "Konsole 整合", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/lldb/kdevlldb.json b/plugins/lldb/kdevlldb.json index 9bf1919241..5f72e035ae 100644 --- a/plugins/lldb/kdevlldb.json +++ b/plugins/lldb/kdevlldb.json @@ -1,65 +1,61 @@ { "KPlugin": { "Category": "Debugging", "Description": "This plugin provides a frontend for LLDB, a next generation, high-performance C/Obj-C/C++ debugger.", "Description[ca@valencia]": "Aquest connector proveeix d'un frontal per al LLDB, un depurador d'alt rendiment per C/Obj-C/C++ d'última generació.", "Description[ca]": "Aquest connector proveeix d'un frontal per al LLDB, un depurador d'alt rendiment per C/Obj-C/C++ d'última generació.", "Description[cs]": "Tento modul poskytuje rozhraní pro LLDB, vysoce výkonný ladicí nástroj nové generace pro C/Obj-C/C++.", "Description[de]": "Dieses Modul stellt eine Oberfläche für LLDB zur Verfügung. LLDB ist ein leistungsfähiger Debugger für C, Obj-C und C++.", - "Description[el]": "Αυτό το πρόσθετο παρέχει ένα περιβάλλον χρήσης για το LLDB, έναν επόμενης γενιάς, υψηλής απόδοσης C/Obj-C/C++ διορθωτή σφαλμάτων.", "Description[en_GB]": "This plugin provides a frontend for LLDB, a next generation, high-performance C/Obj-C/C++ debugger.", "Description[es]": "Este complemento proporciona una interfaz para LLDB, un depurador para C, Obj-C y C++ de nueva generación y alto rendimiento.", - "Description[et]": "See plugin pakub LLDB - järgmise põlvkonna äärmiselt suure jõudlusega C/Obj-C/C++ siluri - kasutajaliidest.", "Description[fr]": "Ce module fournit une interface pour LLDB, un débogueur de nouvelle génération, haute-performance pour le V / Objective-C / C++.", "Description[gl]": "Este complemento fornece unha interface para LLDB, un depurador de nova xeración de alto rendemento para C, Objective-C e C++.", "Description[it]": "Questa estensione fornisce un'interfaccia per LLDB, un debugger di nuova generazione ad alte prestazioni per C/Obj-C/C++.", "Description[nl]": "Deze plugin levert een frontend voor LLDB, een volgende generatie met hoge prestaties voor debuggen van C/Obj-C/C++.", "Description[pl]": "Udostępnia interfejs do LLDB, wysokiej wydajności programu diagnostycznego następnej generacji dla C/Obj-C/C++.", - "Description[pt]": "Este 'plugin' oferece uma interface para o LLDB, um depurador de código para C/Obj-C/C++ de nova geração, com alta performance.", + "Description[pt]": "Este 'plugin' oferece uma interface para o LLDB, um depurador de código para C/Obj-C/C++.", "Description[pt_BR]": "Este plugin oferece uma interface para o LLDB, um depurador de código com alta performance para C/Obj-C/C++ de nova geração.", "Description[sk]": "Tento modul poskytuje rozhranie pre LLDB, novú generáciu vysokovýkonného debuggera C/Obj-C/C++.", "Description[sl]": "Ta vstavek prinaša začelje za LLDB, novo generacijo visoko zmogljivega razhroščevalnika za C/Obj-C/C++.", "Description[sv]": "Insticksprogrammet tillhandahåller ett gränssnitt till LLDB, nästa generations avlusare för C, Obj-C, C++ med hög prestanda.", "Description[tr]": "Bu eklenti gelecek nesil, yüksek performanslı C/Obj-C/C++ hata ayıklayıcı olan LLDB için ön yüz sağlar.", "Description[uk]": "Цей додаток є обгорткою до LLDB, високошвидкісного засобу діагностики коду мовами C/Obj-C/C++.", "Description[x-test]": "xxThis plugin provides a frontend for LLDB, a next generation, high-performance C/Obj-C/C++ debugger.xx", "Description[zh_CN]": "此插件提供了 GDB 前端,GDB 是一个源码级的 C、C++ 等多语言的调试器。", "Icon": "kdevelop", "Id": "kdevlldb", "License": "GPL", "Name": "LLDB Support", "Name[ca@valencia]": "Implementació del LLDB", "Name[ca]": "Implementació del LLDB", "Name[cs]": "Podpora LLDB", "Name[de]": "Unterstützung für LLDB", - "Name[el]": "Υποστήριξη LLDB", "Name[en_GB]": "LLDB Support", "Name[es]": "Implementación de LLDB", - "Name[et]": "LLDB toetus", "Name[fr]": "Prise en charge de LLDB", "Name[gl]": "Compatibilidade con LLDB.", "Name[it]": "Supporto per LLDB", "Name[nl]": "Ondersteuning voor LLDB", "Name[pl]": "Obsługa LLDB", "Name[pt]": "Suporte para o LLDB", "Name[pt_BR]": "Suporte ao LLDB", "Name[sk]": "Podpora LLDB", "Name[sl]": "Podpora za LLDB", "Name[sv]": "LLDB-stöd", "Name[tr]": "LLDB Desteği", "Name[uk]": "Підтримка LLDB", "Name[x-test]": "xxLLDB Supportxx", "Name[zh_CN]": "LLDB 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-IRequired": [ "org.kdevelop.IExecutePlugin" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IStatus" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/lldb/unittests/test_lldb.cpp b/plugins/lldb/unittests/test_lldb.cpp index 51242cd95b..a006983109 100644 --- a/plugins/lldb/unittests/test_lldb.cpp +++ b/plugins/lldb/unittests/test_lldb.cpp @@ -1,1906 +1,1906 @@ /* * Unit tests for LLDB debugger plugin Copyright 2009 Niko Sams Copyright 2013 Vlas Puhov * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "test_lldb.h" #include "controllers/framestackmodel.h" #include "debugsession.h" #include "tests/debuggers-tests-config.h" #include "tests/testhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WAIT_FOR_STATE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR_STATE_AND_IDLE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__, true)) return; } while (0) #define WAIT_FOR_A_WHILE(session, ms) \ do { if (!KDevMI::waitForAWhile((session), (ms), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR(session, condition) \ do { \ KDevMI::TestWaiter w((session), #condition, __FILE__, __LINE__); \ while (w.waitUnless((condition))) /* nothing */ ; \ } while(0) #define COMPARE_DATA(index, expected) \ do { if (!KDevMI::compareData((index), (expected), __FILE__, __LINE__)) return; } while (0) #define SKIP_IF_ATTACH_FORBIDDEN() \ do { \ if (KDevMI::isAttachForbidden(__FILE__, __LINE__)) \ return; \ } while(0) using namespace KDevelop; using namespace KDevMI::LLDB; using KDevMI::findExecutable; using KDevMI::findFile; using KDevMI::findSourceFile; namespace { class WritableEnvironmentProfileList : public EnvironmentProfileList { public: explicit WritableEnvironmentProfileList(KConfig* config) : EnvironmentProfileList(config) {} using EnvironmentProfileList::variables; using EnvironmentProfileList::saveSettings; using EnvironmentProfileList::removeProfile; }; class TestLaunchConfiguration : public ILaunchConfiguration { public: explicit TestLaunchConfiguration(const QUrl& executable = findExecutable(QStringLiteral("debuggee_debugee")), const QUrl& workingDirectory = QUrl()) { qDebug() << "FIND" << executable; c = KSharedConfig::openConfig(); c->deleteGroup("launch"); cfg = c->group("launch"); cfg.writeEntry("isExecutable", true); cfg.writeEntry("Executable", executable); cfg.writeEntry("Working Directory", workingDirectory); } const KConfigGroup config() const override { return cfg; } KConfigGroup config() override { return cfg; }; QString name() const override { return QStringLiteral("Test-Launch"); } KDevelop::IProject* project() const override { return nullptr; } KDevelop::LaunchConfigurationType* type() const override { return nullptr; } KConfig* rootConfig() { return c.data(); } private: KConfigGroup cfg; KSharedConfigPtr c; }; class TestFrameStackModel : public LldbFrameStackModel { Q_OBJECT public: explicit TestFrameStackModel(DebugSession* session) : LldbFrameStackModel(session), fetchFramesCalled(0), fetchThreadsCalled(0) {} void fetchFrames(int threadNumber, int from, int to) override { fetchFramesCalled++; LldbFrameStackModel::fetchFrames(threadNumber, from, to); } void fetchThreads() override { fetchThreadsCalled++; LldbFrameStackModel::fetchThreads(); } int fetchFramesCalled; int fetchThreadsCalled; }; class TestDebugSession : public DebugSession { Q_OBJECT public: TestDebugSession() : DebugSession() { // explicit set formatter path to force use in-tree formatters, not the one installed in system. auto formatter = findFile(LLDB_SRC_DIR, "formatters/all.py"); setFormatterPath(formatter); setSourceInitFile(false); m_frameStackModel = new TestFrameStackModel(this); KDevelop::ICore::self()->debugController()->addSession(this); } TestFrameStackModel* frameStackModel() const override { return m_frameStackModel; } private: TestFrameStackModel* m_frameStackModel; }; } // end of anonymous namespace BreakpointModel* LldbTest::breakpoints() { return m_core->debugController()->breakpointModel(); } VariableCollection *LldbTest::variableCollection() { return m_core->debugController()->variableCollection(); } Variable *LldbTest::watchVariableAt(int i) { auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); auto idx = variableCollection()->index(i, 0, watchRoot); return qobject_cast(variableCollection()->itemForIndex(idx)); } QModelIndex LldbTest::localVariableIndexAt(int i, int col) { auto localRoot = variableCollection()->indexForItem(variableCollection()->locals(), 0); return variableCollection()->index(i, col, localRoot); } // Called before the first testfunction is executed void LldbTest::initTestCase() { AutoTestShell::init(); m_core = TestCore::initialize(Core::NoUi); m_iface = m_core->pluginController() ->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute")) ->extension(); Q_ASSERT(m_iface); m_debugeeFileName = findSourceFile("debugee.cpp"); const QString lldbMiExecutable = QStandardPaths::findExecutable(QStringLiteral("lldb-mi")); if (lldbMiExecutable.isEmpty()) { QSKIP("Skipping, lldb-mi not available"); } } // Called after the last testfunction was executed void LldbTest::cleanupTestCase() { TestCore::shutdown(); } // Called before each testfunction is executed void LldbTest::init() { //remove all breakpoints - so we can set our own in the test KConfigGroup bpCfg = KSharedConfig::openConfig()->group("breakpoints"); bpCfg.writeEntry("number", 0); bpCfg.sync(); breakpoints()->removeRows(0, breakpoints()->rowCount()); while (variableCollection()->watches()->childCount() > 0) { auto var = watchVariableAt(0); if (!var) break; var->die(); } } void LldbTest::cleanup() { // Called after every testfunction } void LldbTest::testStdout() { auto *session = new TestDebugSession; QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); TestLaunchConfiguration cfg; QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { - QList arguments = outputSpy.takeFirst(); + const QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << "Hello, world!" << "Hello"); } void LldbTest::testEnvironmentSet() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeechoenv"))); cfg.config().writeEntry("EnvironmentGroup", "LldbTestGroup"); WritableEnvironmentProfileList envProfiles(cfg.rootConfig()); envProfiles.removeProfile(QStringLiteral("LldbTestGroup")); auto &envs = envProfiles.variables(QStringLiteral("LldbTestGroup")); envs[QStringLiteral("VariableA")] = QStringLiteral("-A' \" complex --value"); envs[QStringLiteral("VariableB")] = QStringLiteral("-B' \" complex --value"); envProfiles.saveSettings(cfg.rootConfig()); QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { - QList arguments = outputSpy.takeFirst(); + const QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << "-A' \" complex --value" << "-B' \" complex --value"); } void LldbTest::testBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 29); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); QCOMPARE(session->currentLine(), 29); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); } void LldbTest::testBreakOnStart() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; cfg.config().writeEntry(KDevMI::Config::BreakOnStartEntry, true); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); // line 28 is the start of main function in debugee.cpp QCOMPARE(session->currentLine(), 27); // currentLine is zero-based session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testDisableBreakpoint() { QSKIP("Skipping... In lldb-mi -d flag has no effect when mixed with -f"); //Description: We must stop only on the third breakpoint int firstBreakLine=28; int secondBreakLine=23; int thirdBreakLine=24; int fourthBreakLine=31; auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint *b; b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), firstBreakLine); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //this is needed to emulate debug from GUI. If we are in edit mode, the debugSession doesn't exist. m_core->debugController()->breakpointModel()->blockSignals(true); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), secondBreakLine); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //all disabled breakpoints were added auto *thirdBreak = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), thirdBreakLine); m_core->debugController()->breakpointModel()->blockSignals(false); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), thirdBreak->line()); //disable existing breakpoint thirdBreak->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //add another disabled breakpoint b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), fourthBreakLine); WAIT_FOR_A_WHILE(session, 300); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); WAIT_FOR_A_WHILE(session, 300); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testChangeLocationBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; auto *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 27); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 27); WAIT_FOR_A_WHILE(session, 100); b->setLine(28); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_A_WHILE(session, 100); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 28); WAIT_FOR_A_WHILE(session, 500); breakpoints()->setData(breakpoints()->index(0, KDevelop::Breakpoint::LocationColumn), QString(m_debugeeFileName+":30")); QCOMPARE(b->line(), 29); WAIT_FOR_A_WHILE(session, 100); QCOMPARE(b->line(), 29); session->run(); WAIT_FOR_A_WHILE(session, 100); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 29); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testDeleteBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; QCOMPARE(breakpoints()->rowCount(), 0); //add breakpoint before startDebugging breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); QCOMPARE(breakpoints()->rowCount(), 1); breakpoints()->removeRow(0); QCOMPARE(breakpoints()->rowCount(), 0); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 22); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testPendingBreakpoint() { QSKIP("Skipping... Pending breakpoint not work on lldb-mi"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); auto * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("test_lldb.cpp")), 10); QCOMPARE(b->state(), Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), Breakpoint::PendingState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testUpdateBreakpoint() { // Description: user might insert breakpoints using lldb console. model should // pick up the manually set breakpoint auto *session = new TestDebugSession; TestLaunchConfiguration cfg; // break at line 29 auto b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); QCOMPARE(breakpoints()->rowCount(), 1); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop at line 29 session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop after step QCOMPARE(session->currentLine(), 23-1); // at the beginning of foo():23: ++i; session->addUserCommand(QStringLiteral("break set --file %1 --line %2").arg(m_debugeeFileName).arg(33)); WAIT_FOR_A_WHILE(session, 20); QCOMPARE(breakpoints()->rowCount(), 2); b = breakpoints()->breakpoint(1); QCOMPARE(b->url(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(b->line(), 33-1); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop at line 25 QCOMPARE(session->currentLine(), 33-1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testIgnoreHitsBreakpoint() { QSKIP("Skipping... lldb-mi doesn't provide breakpoint hit count update"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b1 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); b1->setIgnoreHits(1); KDevelop::Breakpoint * b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 22); QVERIFY(session->startDebugging(&cfg, m_iface)); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b2->hitCount() == 1); b2->setIgnoreHits(1); session->run(); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b1->hitCount() == 1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testConditionBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; auto b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 39); b->setCondition(QStringLiteral("x[0] == 'H'")); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 23); b->setCondition(QStringLiteral("i==2")); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR(session, session->state() == DebugSession::PausedState && session->currentLine() == 24); b->setCondition(QStringLiteral("i == 0")); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 39); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnWriteBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); breakpoints()->addWatchpoint(QStringLiteral("i")); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnWriteWithConditionBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); KDevelop::Breakpoint *b = breakpoints()->addWatchpoint(QStringLiteral("i")); b->setCondition(QStringLiteral("i==2")); QTest::qWait(100); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnReadBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addReadWatchpoint(QStringLiteral("foo::i")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnReadBreakpoint2() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); breakpoints()->addReadWatchpoint(QStringLiteral("i")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 22); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnAccessBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); breakpoints()->addAccessWatchpoint(QStringLiteral("i")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 22); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testInsertBreakpointWhileRunning() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile("debugeeslow.cpp"); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::ActiveState); WAIT_FOR_A_WHILE(session, 2000); qDebug() << "adding breakpoint"; KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); WAIT_FOR_A_WHILE(session, 500); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(session->currentLine(), 25); b->setDeleted(); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testInsertBreakpointWhileRunningMultiple() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile("debugeeslow.cpp"); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::ActiveState); WAIT_FOR_A_WHILE(session, 2000); qDebug() << "adding breakpoint"; auto b1 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 24); auto b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); WAIT_FOR_A_WHILE(session, 500); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(session->currentLine(), 25); b1->setDeleted(); b2->setDeleted(); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testInsertBreakpointFunctionName() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 27); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testManualBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 27); breakpoints()->removeRows(0, 1); WAIT_FOR_A_WHILE(session, 100); QCOMPARE(breakpoints()->rowCount(), 0); session->addCommand(MI::NonMI, QStringLiteral("break set --file debugee.cpp --line 23")); WAIT_FOR_A_WHILE(session, 100); QCOMPARE(breakpoints()->rowCount(), 1); auto b = breakpoints()->breakpoint(0); QCOMPARE(b->line(), 22); session->addCommand(MI::NonMI, QStringLiteral("break disable 2")); session->addCommand(MI::NonMI, QStringLiteral("break modify -c 'i == 1' 2")); session->addCommand(MI::NonMI, QStringLiteral("break modify -i 1 2")); WAIT_FOR_A_WHILE(session, 1000); QCOMPARE(b->enabled(), false); QEXPECT_FAIL("", "LLDB 4.0 does not report condition in mi response", Continue); QCOMPARE(b->condition(), QString("i == 1")); QEXPECT_FAIL("", "LLDB 4.0 does not report ignore hits in mi response", Continue); QCOMPARE(b->ignoreHits(), 1); session->addCommand(MI::NonMI, QStringLiteral("break delete 2")); WAIT_FOR_A_WHILE(session, 100); QEXPECT_FAIL("", "LLDB 4.0 does not report breakpoint deletion as mi notification", Continue); QCOMPARE(breakpoints()->rowCount(), 0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } //Bug 201771 void LldbTest::testInsertAndRemoveBreakpointWhileRunning() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile("debugeeslow.cpp"); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::ActiveState); WAIT_FOR_A_WHILE(session, 1000); KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); WAIT_FOR_A_WHILE(session, 200); // wait for feedback notification from lldb-mi b->setDeleted(); WAIT_FOR_A_WHILE(session, 3000); // give slow debugee extra time to run WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testPickupManuallyInsertedBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->addCommand(MI::NonMI, QStringLiteral("break set --file debugee.cpp --line 32")); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 2); QCOMPARE(breakpoints()->rowCount(), 2); KDevelop::Breakpoint *b = breakpoints()->breakpoint(1); QVERIFY(b); QCOMPARE(b->line(), 31); //we start with 0, gdb with 1 QCOMPARE(b->url().fileName(), QString("debugee.cpp")); } //Bug 270970 void LldbTest::testPickupManuallyInsertedBreakpointOnlyOnce() { auto *session = new TestDebugSession; QString sourceFile = findSourceFile("debugee.cpp"); //inject here, so it behaves similar like a command from .lldbinit QTemporaryFile configScript; configScript.open(); configScript.write(QStringLiteral("break set --file %0 --line 32\n").arg(sourceFile).toLocal8Bit()); configScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::LldbConfigScriptEntry, QUrl::fromLocalFile(configScript.fileName())); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(QStringLiteral("debugee.cpp")), 31); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakpointWithSpaceInPath() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeespace"))); KConfigGroup grp = cfg.config(); QString fileName = findSourceFile("debugee space.cpp"); KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 20); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 20); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakpointDisabledOnStart() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; auto b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 23); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 29); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 34); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QEXPECT_FAIL("", "See LLDB bug 28703: -d flag has no effect", Abort); QCOMPARE(session->currentLine(), 29); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 34); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testMultipleLocationsBreakpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeemultilocbreakpoint"))); breakpoints()->addCodeBreakpoint(QStringLiteral("aPlusB")); //TODO check if the additional location breakpoint is added QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 19); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testMultipleBreakpoint() { auto *session = new TestDebugSession; //there'll be about 3-4 breakpoints, but we treat it like one. TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeemultiplebreakpoint"))); auto b = breakpoints()->addCodeBreakpoint(QStringLiteral("debugeemultiplebreakpoint.cpp:52")); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testRegularExpressionBreakpoint() { QSKIP("Skipping... lldb has only one breakpoint for multiple locations" " (and lldb-mi returns the first one), not support this yet"); auto *session = new TestDebugSession; TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeemultilocbreakpoint"))); breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->addCommand(MI::NonMI, QStringLiteral("break set --func-regex .*aPl.*B")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 3); session->addCommand(MI::BreakDelete, QString()); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testChangeBreakpointWhileRunning() { QSKIP("Skipping... lldb-mi command -break-enable doesn't enable breakpoint"); auto *session = new TestDebugSession; TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeeslow"))); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QStringLiteral("debugeeslow.cpp:25")); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QVERIFY(session->currentLine() >= 24 && session->currentLine() <= 26 ); session->run(); WAIT_FOR_STATE(session, DebugSession::ActiveState); qDebug() << "Disabling breakpoint"; b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //to make one loop WAIT_FOR_A_WHILE(session, 2500); qDebug() << "Waiting for active"; WAIT_FOR_STATE(session, DebugSession::ActiveState); qDebug() << "Enabling breakpoint"; // Use native user command works, but not through -break-enable, which is triggered by setData session->addCommand(MI::NonMI, QStringLiteral("break enable")); //b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testCatchpoint() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeexception"))); session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel* fsModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("debugeeexception.cpp")), 29); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(fsModel->currentFrame(), 0); QCOMPARE(session->currentLine(), 29); session->addCommand(MI::NonMI, QStringLiteral("break set -E c++")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); const auto frames = fsModel->frames(fsModel->currentThread()); QVERIFY(frames.size() >= 2); // frame 0 is somewhere inside libstdc++ QCOMPARE(frames[1].file, QUrl::fromLocalFile(findSourceFile("debugeeexception.cpp"))); QCOMPARE(frames[1].line, 22); QCOMPARE(breakpoints()->rowCount(),2); QVERIFY(!breakpoints()->breakpoint(0)->location().isEmpty()); QVERIFY(!breakpoints()->breakpoint(1)->location().isEmpty()); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testShowStepInSource() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; QSignalSpy showStepInSourceSpy(session, &TestDebugSession::showStepInSource); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 29); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); { QCOMPARE(showStepInSourceSpy.count(), 3); QList arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().toUrl(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 29); arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().toUrl(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 22); arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().toUrl(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 23); } } void LldbTest::testStack() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo()"); QCOMPARE(stackModel->rowCount(tIdx), 4); QCOMPARE(stackModel->columnCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(0, 2, tIdx), m_debugeeFileName+":23"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(1, 2, tIdx), m_debugeeFileName+":29"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(3, 0, tIdx), "3"); COMPARE_DATA(stackModel->index(3, 1, tIdx), "_start"); session->stepOut(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), m_debugeeFileName+":30"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "_start"); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testStackFetchMore() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeerecursion"))); QString fileName = findSourceFile("debugeerecursion.cpp"); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->frameStackModel()->fetchFramesCalled, 1); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo()"); QCOMPARE(stackModel->rowCount(tIdx), 21); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(0, 2, tIdx), fileName+":26"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(1, 2, tIdx), fileName+":24"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(2, 2, tIdx), fileName+":24"); COMPARE_DATA(stackModel->index(19, 0, tIdx), "19"); COMPARE_DATA(stackModel->index(20, 0, tIdx), "20"); stackModel->fetchMoreFrames(); WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 2); QCOMPARE(stackModel->rowCount(tIdx), 41); COMPARE_DATA(stackModel->index(20, 0, tIdx), "20"); COMPARE_DATA(stackModel->index(21, 0, tIdx), "21"); COMPARE_DATA(stackModel->index(22, 0, tIdx), "22"); COMPARE_DATA(stackModel->index(39, 0, tIdx), "39"); COMPARE_DATA(stackModel->index(40, 0, tIdx), "40"); stackModel->fetchMoreFrames(); WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 3); QCOMPARE(stackModel->rowCount(tIdx), 121); COMPARE_DATA(stackModel->index(40, 0, tIdx), "40"); COMPARE_DATA(stackModel->index(41, 0, tIdx), "41"); COMPARE_DATA(stackModel->index(42, 0, tIdx), "42"); COMPARE_DATA(stackModel->index(119, 0, tIdx), "119"); COMPARE_DATA(stackModel->index(120, 0, tIdx), "120"); stackModel->fetchMoreFrames(); WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 4); QCOMPARE(stackModel->rowCount(tIdx), 301); COMPARE_DATA(stackModel->index(120, 0, tIdx), "120"); COMPARE_DATA(stackModel->index(121, 0, tIdx), "121"); COMPARE_DATA(stackModel->index(122, 0, tIdx), "122"); COMPARE_DATA(stackModel->index(298, 0, tIdx), "298"); COMPARE_DATA(stackModel->index(298, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(298, 2, tIdx), fileName+":30"); COMPARE_DATA(stackModel->index(299, 0, tIdx), "299"); COMPARE_DATA(stackModel->index(299, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(300, 0, tIdx), "300"); COMPARE_DATA(stackModel->index(300, 1, tIdx), "_start"); stackModel->fetchMoreFrames(); //nothing to fetch, we are at the end WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 4); QCOMPARE(stackModel->rowCount(tIdx), 301); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testStackDeactivateAndActive() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); session->stepOut(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), m_debugeeFileName+":30"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "_start"); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testStackSwitchThread() { QSKIP("Skipping... lldb-mi crashes when break at a location with multiple threads running"); auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeethreads"))); QString fileName = findSourceFile("debugeethreads.cpp"); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->rowCount(), 4); QModelIndex tIdx = stackModel->index(0,0); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 1); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), fileName+":39"); tIdx = stackModel->index(1,0); QVERIFY(stackModel->data(tIdx).toString().startsWith("#2 at ")); stackModel->setCurrentThread(2); WAIT_FOR_A_WHILE(session, 200); int rows = stackModel->rowCount(tIdx); QVERIFY(rows > 3); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testAttach() { SKIP_IF_ATTACH_FORBIDDEN(); QString fileName = findSourceFile("debugeeslow.cpp"); KProcess debugeeProcess; debugeeProcess << QStringLiteral("nice") << findExecutable(QStringLiteral("debuggee_debugeeslow")).toLocalFile(); debugeeProcess.start(); QVERIFY(debugeeProcess.waitForStarted()); QTest::qWait(100); auto *session = new TestDebugSession; session->attachToProcess(debugeeProcess.pid()); WAIT_FOR_A_WHILE(session, 100); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 35); // lldb-mi silently stops when attaching to a process. Force it continue to run. session->addCommand(MI::ExecContinue, QString(), MI::CmdMaybeStartsRunning); WAIT_FOR_A_WHILE(session, 2000); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testRemoteDebugging() { KProcess gdbServer; gdbServer << QStringLiteral("lldb-server") << QStringLiteral("gdbserver") << QStringLiteral("*:1234"); gdbServer.start(); QVERIFY(gdbServer.waitForStarted()); auto *session = new TestDebugSession; TestLaunchConfiguration cfg; cfg.config().writeEntry(Config::LldbRemoteDebuggingEntry, true); cfg.config().writeEntry(Config::LldbRemoteServerEntry, "localhost:1234"); cfg.config().writeEntry(Config::LldbRemotePathEntry, "/tmp"); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 34); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testCoreFile() { QFileInfo f(QStringLiteral("core")); f.setCaching(false); // don't cache information if (f.exists()) { QVERIFY(QFile::remove(f.canonicalFilePath())); } KProcess debugeeProcess; debugeeProcess.setOutputChannelMode(KProcess::MergedChannels); debugeeProcess << QStringLiteral("bash") << QStringLiteral("-c") << "ulimit -c unlimited; " + findExecutable(QStringLiteral("debuggee_crash")).toLocalFile(); debugeeProcess.start(); debugeeProcess.waitForFinished(); qDebug() << debugeeProcess.readAll(); bool coreFileFound = f.exists(); if (!coreFileFound) { // Try to use coredumpctl qDebug() << "try to use coredumpctl"; auto coredumpctl = QStandardPaths::findExecutable(QStringLiteral("coredumpctl")); if (!coredumpctl.isEmpty()) { KProcess::execute(coredumpctl, {"-1", "-o", f.absoluteFilePath(), "dump", "debuggee_crash"}); // coredumpctl seems to create an empty file "core" even if no cores can be delivered // (like when run inside docker containers as on KDE CI or with kernel.core_pattern=|/dev/null) // so also check for size != 0 coreFileFound = f.exists() && (f.size() > 0); } } if (!coreFileFound) QSKIP("no core dump found, check your system configuration (see /proc/sys/kernel/core_pattern).", SkipSingle); auto *session = new TestDebugSession; session->examineCoreFile(findExecutable(QStringLiteral("debuggee_crash")), QUrl::fromLocalFile(f.canonicalFilePath())); TestFrameStackModel *stackModel = session->frameStackModel(); WAIT_FOR_STATE(session, DebugSession::StoppedState); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo()"); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesLocals() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(variableCollection()->rowCount(), 2); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); COMPARE_DATA(variableCollection()->index(0, 1, i), "2"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesLocalsStruct() { auto *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 1000); QModelIndex i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); int structIndex = 0; for(int j=0; j<3; ++j) { if (variableCollection()->index(j, 0, i).data().toString() == QLatin1String("ts")) { structIndex = j; } } COMPARE_DATA(variableCollection()->index(structIndex, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(structIndex, 1, i), "{...}"); QModelIndex ts = variableCollection()->index(structIndex, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, ts), "..."); variableCollection()->expanded(ts); WAIT_FOR_A_WHILE(session, 100); COMPARE_DATA(variableCollection()->index(0, 0, ts), "a"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "0"); COMPARE_DATA(variableCollection()->index(1, 0, ts), "b"); COMPARE_DATA(variableCollection()->index(1, 1, ts), "1"); COMPARE_DATA(variableCollection()->index(2, 0, ts), "c"); COMPARE_DATA(variableCollection()->index(2, 1, ts), "2"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 1000); COMPARE_DATA(variableCollection()->index(structIndex, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(structIndex, 1, i), "{...}"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesWatches() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; m_core->debugController()->variableCollection()->variableWidgetShown(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("ts")); WAIT_FOR_A_WHILE(session, 300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(0, 1, i), "{...}"); QModelIndex ts = variableCollection()->index(0, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, ts), "..."); variableCollection()->expanded(ts); WAIT_FOR_A_WHILE(session, 100); COMPARE_DATA(variableCollection()->index(0, 0, ts), "a"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "0"); COMPARE_DATA(variableCollection()->index(1, 0, ts), "b"); COMPARE_DATA(variableCollection()->index(1, 1, ts), "1"); COMPARE_DATA(variableCollection()->index(2, 0, ts), "c"); COMPARE_DATA(variableCollection()->index(2, 1, ts), "2"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 100); COMPARE_DATA(variableCollection()->index(0, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(0, 1, i), "{...}"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesWatchesQuotes() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); // the unquoted string (the actual content): t\"t // quoted string (what we would write as a c string): "t\\\"t" // written in source file: R"("t\\\"t")" const QString testString(QStringLiteral("t\\\"t")); // the actual content const QString quotedTestString(QStringLiteral(R"("t\\\"t")")); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(quotedTestString); //just a constant string WAIT_FOR_A_WHILE(session, 3000); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), quotedTestString); QEXPECT_FAIL("", "LLDB 4.0 cannot deal with string literal in expression when debugging, causing memory access error", Abort); COMPARE_DATA(variableCollection()->index(0, 1, i), quotedTestString); QModelIndex testStr = variableCollection()->index(0, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, testStr), "..."); variableCollection()->expanded(testStr); WAIT_FOR_A_WHILE(session, 100); int len = testString.length(); for (int ind = 0; ind < len; ind++) { COMPARE_DATA(variableCollection()->index(ind, 0, testStr), QStringLiteral("[%0]").arg(ind)); QChar c = testString.at(ind); QString value = QString::number(c.toLatin1()) + " '" + c + "'"; COMPARE_DATA(variableCollection()->index(ind, 1, testStr), value); } COMPARE_DATA(variableCollection()->index(len, 0, testStr), QStringLiteral("[%0]").arg(len)); COMPARE_DATA(variableCollection()->index(len, 1, testStr), "0 '\\0'"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesWatchesTwoSessions() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("ts")); WAIT_FOR_A_WHILE(session, 300); QModelIndex ts = variableCollection()->index(0, 0, variableCollection()->index(0, 0)); variableCollection()->expanded(ts); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //check if variable is marked as out-of-scope QCOMPARE(variableCollection()->watches()->childCount(), 1); auto v = qobject_cast(watchVariableAt(0)); QVERIFY(v); QVERIFY(!v->inScope()); QCOMPARE(v->childCount(), 3); v = qobject_cast(v->child(0)); QVERIFY(!v->inScope()); //start a second debug session session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(variableCollection()->watches()->childCount(), 1); ts = variableCollection()->index(0, 0, variableCollection()->index(0, 0)); v = qobject_cast(watchVariableAt(0)); QVERIFY(v); QVERIFY(v->inScope()); QCOMPARE(v->childCount(), 3); v = qobject_cast(v->child(0)); QVERIFY(v->inScope()); COMPARE_DATA(variableCollection()->indexForItem(v, 1), QString::number(0)); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //check if variable is marked as out-of-scope v = qobject_cast(watchVariableAt(0)); QVERIFY(!v->inScope()); QVERIFY(!qobject_cast(v->child(0))->inScope()); } void LldbTest::testVariablesStopDebugger() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesStartSecondSession() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesSwitchFrame() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); // only non-static variable works COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 200); i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); COMPARE_DATA(variableCollection()->index(2, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(2, 1, i), "1"); COMPARE_DATA(variableCollection()->index(3, 0, i), "argv"); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesQuicklySwitchFrame() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); // only non-static variable works COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 300); stackModel->setCurrentFrame(0); WAIT_FOR_A_WHILE(session, 1); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 1); stackModel->setCurrentFrame(0); WAIT_FOR_A_WHILE(session, 1); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 500); i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); QStringList locs; for (int j = 0; j < variableCollection()->rowCount(i); ++j) { locs << variableCollection()->index(j, 0, i).data().toString(); } QVERIFY(locs.contains("argc")); QVERIFY(locs.contains("argv")); QVERIFY(locs.contains("x")); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testSwitchFrameLldbConsole() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->currentFrame(), 0); stackModel->setCurrentFrame(1); QCOMPARE(stackModel->currentFrame(), 1); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(stackModel->currentFrame(), 1); session->addUserCommand(QStringLiteral("print i")); WAIT_FOR_A_WHILE(session, 500); //currentFrame must not reset to 0; Bug 222882 QCOMPARE(stackModel->currentFrame(), 1); } void LldbTest::testSegfaultDebugee() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_crash"))); session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); QString fileName = findSourceFile("debugeecrash.cpp"); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 23); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } //Bug 274390 void LldbTest::testCommandOrderFastStepping() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeqt"))); breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); for(int i=0; i<20; i++) { session->stepInto(); } WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testRunLldbScript() { auto *session = new TestDebugSession; QTemporaryFile runScript; runScript.open(); runScript.write(QStringLiteral("break set --file %1 --line 35\n").arg(findSourceFile("debugee.cpp")).toUtf8()); runScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::LldbConfigScriptEntry, QUrl::fromLocalFile(runScript.fileName())); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBug301287() { auto *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("argc")); WAIT_FOR_A_WHILE(session, 300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //start second debug session (same cfg) session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testDebugInExternalTerminal() { TestLaunchConfiguration cfg; const QStringList consoles { "konsole", "xterm", "xfce4-terminal", "gnome-terminal" }; for (const QString& console : consoles) { if (QStandardPaths::findExecutable(console).isEmpty()) { continue; } auto* session = new TestDebugSession(); cfg.config().writeEntry("External Terminal"/*ExecutePlugin::terminalEntry*/, console); cfg.config().writeEntry("Use External Terminal"/*ExecutePlugin::useTerminalEntry*/, true); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } } void LldbTest::testSpecialPath() { QSKIP("Skipping... lldb-mi itself can't handle path with space in application dir"); auto* session = new TestDebugSession; auto debugee = findExecutable(QStringLiteral("path with space/debuggee_spacedebugee")); TestLaunchConfiguration c(debugee, KIO::upUrl(debugee)); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QStringLiteral("spacedebugee.cpp:30")); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void KDevMI::LLDB::LldbTest::testEnvironmentCd() { auto *session = new TestDebugSession; QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); auto path = KIO::upUrl(findExecutable(QStringLiteral("path with space/debuggee_spacedebugee"))); TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeepath")), path); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { - QList arguments = outputSpy.takeFirst(); + const QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << path.toLocalFile()); } QTEST_MAIN(KDevMI::LLDB::LldbTest) #include "test_lldb.moc" diff --git a/plugins/lldb/unittests/test_lldbformatters.cpp b/plugins/lldb/unittests/test_lldbformatters.cpp index 5819f1ab5a..ced4f8bab8 100644 --- a/plugins/lldb/unittests/test_lldbformatters.cpp +++ b/plugins/lldb/unittests/test_lldbformatters.cpp @@ -1,1032 +1,1032 @@ /* * Unit tests for LLDB debugger plugin * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "test_lldbformatters.h" #include "controllers/variable.h" #include "controllers/variablecontroller.h" #include "debugsession.h" #include "stringhelpers.h" #include "tests/testhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WAIT_FOR_STATE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR_STATE_AND_IDLE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__, true)) return; } while (0) #define WAIT_FOR_A_WHILE_AND_IDLE(session, ms) \ do { if (!KDevMI::waitForAWhile((session), (ms), __FILE__, __LINE__)) return; \ if (!KDevMI::waitForState((session), DebugSession::PausedState, __FILE__, __LINE__, true)) \ return; \ } while (0) #define WAIT_FOR(session, condition) \ do { \ KDevMI::TestWaiter w((session), #condition, __FILE__, __LINE__); \ while (w.waitUnless((condition))) /* nothing */ ; \ } while(0) #define COMPARE_DATA(index, expected) \ do { if (!KDevMI::compareData((index), (expected), __FILE__, __LINE__)) return; } while (0) #define VERIFY_LOCAL(row, name, summary, children) \ do { \ if (!verifyVariable((row), (name), (summary), (children), __FILE__, __LINE__)) \ return; \ } while (0) #define VERIFY_WATCH(row, name, summary, children) \ do { \ if (!verifyVariable((row), (name), (summary), (children), __FILE__, __LINE__, false)) \ return; \ } while (0) using namespace KDevelop; using namespace KDevMI::LLDB; using KDevMI::findExecutable; using KDevMI::findSourceFile; using KDevMI::findFile; using KDevMI::compareData; class TestLaunchConfiguration : public ILaunchConfiguration { public: explicit TestLaunchConfiguration(const QString& executable, const QUrl& workingDirectory = QUrl()) { auto execPath = findExecutable(executable); qDebug() << "FIND" << execPath; c = KSharedConfig::openConfig(); c->deleteGroup("launch"); cfg = c->group("launch"); cfg.writeEntry("isExecutable", true); cfg.writeEntry("Executable", execPath); cfg.writeEntry("Working Directory", workingDirectory); } const KConfigGroup config() const override { return cfg; } KConfigGroup config() override { return cfg; }; QString name() const override { return QStringLiteral("Test-Launch"); } KDevelop::IProject* project() const override { return nullptr; } KDevelop::LaunchConfigurationType* type() const override { return nullptr; } private: KConfigGroup cfg; KSharedConfigPtr c; }; class TestDebugSession : public DebugSession { Q_OBJECT public: TestDebugSession() : DebugSession() { setSourceInitFile(false); // explicit set formatter path to force use in-tree formatters, not the one installed in system. auto formatter = findFile(LLDB_SRC_DIR, "formatters/all.py"); setFormatterPath(formatter); KDevelop::ICore::self()->debugController()->addSession(this); variableController()->setAutoUpdate(IVariableController::UpdateLocals); } }; VariableCollection *LldbFormattersTest::variableCollection() { return m_core->debugController()->variableCollection(); } QModelIndex LldbFormattersTest::watchVariableIndexAt(int i, int col) { auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); return variableCollection()->index(i, col, watchRoot); } QModelIndex LldbFormattersTest::localVariableIndexAt(int i, int col) { auto localRoot = variableCollection()->indexForItem(variableCollection()->locals(), 0); return variableCollection()->index(i, col, localRoot); } // Note: line is zero-based KDevelop::Breakpoint* LldbFormattersTest::addCodeBreakpoint(const QUrl& location, int line) { return m_core->debugController()->breakpointModel()->addCodeBreakpoint(location, line); } // Called before the first testfunction is executed void LldbFormattersTest::initTestCase() { AutoTestShell::init({QStringLiteral("kdevlldb"), QStringLiteral("kdevexecute")}); m_core = TestCore::initialize(Core::NoUi); m_iface = m_core->pluginController() ->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute")) ->extension(); Q_ASSERT(m_iface); const QString lldbMiExecutable = QStandardPaths::findExecutable(QStringLiteral("lldb-mi")); if (lldbMiExecutable.isEmpty()) { QSKIP("Skipping, lldb-mi not available"); } } // Called after the last testfunction was executed void LldbFormattersTest::cleanupTestCase() { TestCore::shutdown(); } // Called before each testfunction is executed void LldbFormattersTest::init() { //remove all breakpoints - so we can set our own in the test KConfigGroup bpCfg = KSharedConfig::openConfig()->group("breakpoints"); bpCfg.writeEntry("number", 0); bpCfg.sync(); auto count = m_core->debugController()->breakpointModel()->rowCount(); m_core->debugController()->breakpointModel()->removeRows(0, count); while (variableCollection()->watches()->childCount() > 0) { auto idx = watchVariableIndexAt(0); auto var = qobject_cast(variableCollection()->itemForIndex(idx)); if (!var) break; var->die(); } m_session = new TestDebugSession; } void LldbFormattersTest::cleanup() { // Called after every testfunction if (m_session) m_session->stopDebugger(); WAIT_FOR_STATE(m_session, DebugSession::EndedState); m_session.clear(); } bool LldbFormattersTest::verifyVariable(int index, const QString &name, const QString &expectedSummary, QStringList expectedChildren, const char *file, int line, bool isLocal, bool useRE, bool unordered) { QList> childrenPairs; childrenPairs.reserve(expectedChildren.size()); if (unordered) { qDebug() << "useRE set to true when unordered = true"; useRE = true; expectedChildren.sort(); for (auto& c : expectedChildren) { childrenPairs << qMakePair(QStringLiteral(R"(^\[\d+\]$)"), c); } } else { for (int i = 0; i != expectedChildren.size(); ++i) { childrenPairs << qMakePair(QStringLiteral("[%0]").arg(i), expectedChildren[i]); } } return verifyVariable(index, name, expectedSummary, childrenPairs, file, line, isLocal, useRE, unordered); } bool LldbFormattersTest::verifyVariable(int index, const QString &name, const QString &expectedSummary, QList> expectedChildren, const char *file, int line, bool isLocal, bool useRE, bool unordered) { QModelIndex varIdx, summaryIdx; if (isLocal) { varIdx = localVariableIndexAt(index, 0); summaryIdx = localVariableIndexAt(index, 1); } else { varIdx = watchVariableIndexAt(index, 0); summaryIdx = watchVariableIndexAt(index, 1); } if (!compareData(varIdx, name, file, line)) { return false; } if (!compareData(summaryIdx, expectedSummary, file, line, useRE)) { return false; } // fetch all children auto var = variableCollection()->itemForIndex(varIdx); auto childCount = 0; while (childCount != variableCollection()->rowCount(varIdx)) { childCount = variableCollection()->rowCount(varIdx); var->fetchMoreChildren(); if (!waitForAWhile(m_session, 50, file, line)) return false; } if (childCount != expectedChildren.length()) { QTest::qFail(qPrintable(QString("'%0' didn't match expected '%1' in %2:%3") .arg(childCount).arg(expectedChildren.length()).arg(file).arg(line)), file, line); return false; } QVector theOrder; theOrder.reserve(childCount); for (int i = 0; i != childCount; ++i) { theOrder.push_back(i); } if (unordered) { qDebug() << "actual list sorted for unordered compare"; std::sort(theOrder.begin(), theOrder.end(), [&](int a, int b){ auto indexA = variableCollection()->index(a, 1, varIdx); auto indexB = variableCollection()->index(b, 1, varIdx); return indexA.model()->data(indexA, Qt::DisplayRole).toString() < indexB.model()->data(indexB, Qt::DisplayRole).toString(); }); std::sort(expectedChildren.begin(), expectedChildren.end(), [](const QPair &a, const QPair &b){ return a.second < b.second; }); qDebug() << "sorted actual order" << theOrder; qDebug() << "sorted expectedChildren" << expectedChildren; } for (int i = 0; i != childCount; ++i) { if (!compareData(variableCollection()->index(theOrder[i], 0, varIdx), expectedChildren[i].first, file, line, useRE)) { return false; } if (!compareData(variableCollection()->index(theOrder[i], 1, varIdx), expectedChildren[i].second, file, line, useRE)) { return false; } } return true; } void LldbFormattersTest::testQChar() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qchar")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qchar.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children << qMakePair(QStringLiteral("ucs"), QStringLiteral("107")); VERIFY_LOCAL(0, "c", "'k'", children); } void LldbFormattersTest::testQString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qstring.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QString expected = QStringLiteral("test最后一个不是特殊字符'\"\\u6211"); QStringList children; - for (auto ch : expected) { + for (auto ch : qAsConst(expected)) { children << Utils::quote(ch, '\''); } VERIFY_LOCAL(0, "s", Utils::quote(expected), children); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 5); expected.append("x"); children << QStringLiteral("'x'"); VERIFY_LOCAL(0, "s", Utils::quote(expected), children); m_session->run(); WAIT_FOR_STATE(m_session, DebugSession::EndedState); } void LldbFormattersTest::testQByteArray() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qbytearray")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qbytearray.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QStringList charlist { R"(-26 '\xe6')", R"(-104 '\x98')", R"(-81 '\xaf')", R"(39 ''')", R"(34 '"')", R"(92 '\')", R"(117 'u')", R"(54 '6')", R"(50 '2')", R"(49 '1')", R"(49 '1')", }; VERIFY_LOCAL(0, "ba", R"("\xe6\x98\xaf'\"\\u6211")", charlist); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 5); charlist << QStringLiteral("120 'x'"); VERIFY_LOCAL(0, "ba", R"("\xe6\x98\xaf'\"\\u6211x")", charlist); m_session->run(); WAIT_FOR_STATE(m_session, DebugSession::EndedState); } void LldbFormattersTest::testQListContainer_data() { QTest::addColumn("container"); QTest::addColumn("unordered"); QTest::newRow("QList") << "QList" << false; QTest::newRow("QQueue") << "QQueue" << false; QTest::newRow("QVector") << "QVector" << false; QTest::newRow("QStack") << "QStack" << false; QTest::newRow("QLinkedList") << "QLinkedList" << false; QTest::newRow("QSet") << "QSet" << true; } void LldbFormattersTest::testQListContainer() { QFETCH(QString, container); QFETCH(bool, unordered); TestLaunchConfiguration cfg(QStringLiteral("debuggee_qlistcontainer")); cfg.config().writeEntry(KDevMI::Config::BreakOnStartEntry, true); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); m_session->addUserCommand(QStringLiteral("break set --func doStuff<%1>()").arg(container)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); m_session->run(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 33); // line 34: intList << 10 << 20; auto var = variableCollection()->watches()->add(QStringLiteral("intList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 34); // line 35: intList << 30; variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral(""), QStringList{"10", "20"}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 36); // line 37: Container stringList; if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral(""), QStringList{"10", "20", "30"}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 37); // line 38: stringList << "a" << "bc"; var = variableCollection()->watches()->add(QStringLiteral("stringList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 38); // line 39: stringList << "d"; variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral(""), QStringList{"\"a\"", "\"bc\""}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 40); // line 41: Container structList; if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral(""), QStringList{"\"a\"", "\"bc\"", "\"d\""}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 41); // line 42: structList << A(QStringLiteral("a"), QStringLiteral("b"), 100, -200); var = variableCollection()->watches()->add(QStringLiteral("structList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->runUntil({}, 43); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 42); // line 43: structList << A(); variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral(""), QStringList{"{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 44); // line 45: Container pointerList; if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral(""), QStringList{"{...}", "{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 45); // line 46: pointerList << new int(1) << new int(2); var = variableCollection()->watches()->add(QStringLiteral("pointerList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 46); // line 47: pointerList << new int(3); variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral(""), QStringList{"^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$"}, __FILE__, __LINE__, false, true, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 47); // line 48: qDeleteAll(pointerList); if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral(""), QStringList{"^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$"}, __FILE__, __LINE__, false, true, unordered)) { return; } var->die(); m_session->stepOver(); // step over qDeleteAll // > m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 50); // line 51: pairList << QPair(1, 2) << qMakePair(2, 3); var = variableCollection()->watches()->add(QStringLiteral("pairList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_WATCH(0, "pairList", "", QStringList{}); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 51); // line 52: pairList << qMakePair(4, 5); variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("pairList"), QStringLiteral(""), QStringList{"{...}", "{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 54); // line 55: int i = 0; if (!verifyVariable(0, QStringLiteral("pairList"), QStringLiteral(""), QStringList{"{...}", "{...}", "{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); } void LldbFormattersTest::testQListPOD() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qlistpod")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qlistpod.cpp")), 30); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); variableCollection()->watches()->add(QStringLiteral("b")); variableCollection()->watches()->add(QStringLiteral("c")); variableCollection()->watches()->add(QStringLiteral("uc")); variableCollection()->watches()->add(QStringLiteral("s")); variableCollection()->watches()->add(QStringLiteral("us")); variableCollection()->watches()->add(QStringLiteral("i")); variableCollection()->watches()->add(QStringLiteral("ui")); variableCollection()->watches()->add(QStringLiteral("l")); variableCollection()->watches()->add(QStringLiteral("ul")); variableCollection()->watches()->add(QStringLiteral("i64")); variableCollection()->watches()->add(QStringLiteral("ui64")); variableCollection()->watches()->add(QStringLiteral("f")); variableCollection()->watches()->add(QStringLiteral("d")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_WATCH(0, "b", "", (QStringList{"false"})); VERIFY_WATCH(1, "c", "", (QStringList{"50 '2'"})); VERIFY_WATCH(2, "uc", "", (QStringList{"50 '2'"})); VERIFY_WATCH(3, "s", "", (QStringList{"50"})); VERIFY_WATCH(4, "us", "", (QStringList{"50"})); VERIFY_WATCH(5, "i", "", (QStringList{"50"})); VERIFY_WATCH(6, "ui", "", (QStringList{"50"})); VERIFY_WATCH(7, "l", "", (QStringList{"50"})); VERIFY_WATCH(8, "ul", "", (QStringList{"50"})); VERIFY_WATCH(9, "i64", "", (QStringList{"50"})); VERIFY_WATCH(10, "ui64", "", (QStringList{"50"})); VERIFY_WATCH(11, "f", "", (QStringList{"50"})); VERIFY_WATCH(12, "d", "", (QStringList{"50"})); } void LldbFormattersTest::testQMapInt() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapint")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapint.cpp")), 6); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "m", "", (QStringList{"(10, 100)", "(20, 200)"})); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); VERIFY_LOCAL(0, "m", "", (QStringList{"(10, 100)", "(20, 200)", "(30, 300)"})); } void LldbFormattersTest::testQMapString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapstring.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); // line 8: m[QStringLiteral("30")] = QStringLiteral("300"); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")"})); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); // line 9: return 0; VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")", "(\"30\", \"300\")"})); } void LldbFormattersTest::testQMapStringBool() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapstringbool")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapstringbool.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", true)", "(\"20\", false)"})); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", true)", "(\"20\", false)", "(\"30\", true)"})); } void LldbFormattersTest::testQHashInt() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qhashint")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qhashint.cpp")), 6); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), QStringList{"(10, 100)", "(20, 200)"}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), QStringList{"(10, 100)", "(20, 200)", "(30, 300)"}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQHashString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qhashstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qhashstring.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")"}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), {"(\"10\", \"100\")", "(\"20\", \"200\")", "(\"30\", \"300\")"}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQSetInt() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qsetint")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qsetint.cpp")), 6); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), QStringList{"10", "20"}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), QStringList{"10", "20", "30"}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQSetString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qsetstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qsetstring.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), QStringList{"\"10\"", "\"20\""}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), {"\"10\"", "\"20\"", "\"30\""}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQDate() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qdate")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qdate.cpp")), 5); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("jd"), QStringLiteral("2455217")}); children.append({QStringLiteral("(ISO)"), QStringLiteral("\"2010-01-20\"")}); children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale) and summary are locale dependent if (!verifyVariable(0, QStringLiteral("d"), QStringLiteral(".+"), children, __FILE__, __LINE__, true, true, false)) { return; } } void LldbFormattersTest::testQTime() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qtime")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qtime.cpp")), 5); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("mds"), QStringLiteral("55810123")}); children.append({QStringLiteral("(ISO)"), QStringLiteral("\"15:30:10.000123\"")}); children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale) and summary are locale dependent if (!verifyVariable(0, QStringLiteral("t"), QStringLiteral(".+"), children, __FILE__, __LINE__, true, true, false)) { return; } } void LldbFormattersTest::testQDateTime() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qdatetime")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qdatetime.cpp")), 5); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("toTime_t"), QStringLiteral("1264019473")}); children.append({QStringLiteral("(ISO)"), QStringLiteral("\"2010-01-20 20:31:13\"")}); children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale), (UTC) and summary are locale dependent children.append({QStringLiteral("(UTC)"), QStringLiteral("\".+\"")}); if (!verifyVariable(0, QStringLiteral("dt"), QStringLiteral(".+"), children, __FILE__, __LINE__, true, true, false)) { return; } } void LldbFormattersTest::testQUrl() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qurl")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qurl.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("(port)"), QStringLiteral("12345")}); children.append({QStringLiteral("(scheme)"), QStringLiteral("\"http\"")}); children.append({QStringLiteral("(userName)"), QStringLiteral("\"user\"")}); children.append({QStringLiteral("(password)"), QStringLiteral("")}); children.append({QStringLiteral("(host)"), QStringLiteral("\"www.kdevelop.org\"")}); children.append({QStringLiteral("(path)"), QStringLiteral("\"/foo\"")}); children.append({QStringLiteral("(query)"), QStringLiteral("\"xyz=bar\"")}); children.append({QStringLiteral("(fragment)"), QStringLiteral("\"asdf\"")}); VERIFY_LOCAL(0, "u", "\"http://user@www.kdevelop.org:12345/foo?xyz=bar#asdf\"", children); } void LldbFormattersTest::testQUuid() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_quuid")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("quuid.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "id", "QUuid({9ec3b70b-d105-42bf-b3b4-656e44d2e223})", (QStringList{})); } void LldbFormattersTest::testKTextEditorTypes() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_ktexteditortypes")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("ktexteditortypes.cpp")), 8); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); variableCollection()->watches()->add(QStringLiteral("cursor")); variableCollection()->watches()->add(QStringLiteral("range")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> cursorChildren; cursorChildren.append({QStringLiteral("m_line"), QStringLiteral("1")}); cursorChildren.append({QStringLiteral("m_column"), QStringLiteral("1")}); QList> rangeChildren; rangeChildren.append({QStringLiteral("m_start"), QStringLiteral("(1, 1)")}); rangeChildren.append({QStringLiteral("m_end"), QStringLiteral("(2, 2)")}); VERIFY_WATCH(0, "cursor", "(1, 1)", cursorChildren); VERIFY_WATCH(1, "range", "[(1, 1) -> (2, 2)]", rangeChildren); } void LldbFormattersTest::testKDevelopTypes() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_kdeveloptypes")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("kdeveloptypes.cpp")), 11); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); variableCollection()->watches()->add(QStringLiteral("path1")); variableCollection()->watches()->add(QStringLiteral("path2")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> path1Children; path1Children.append({QStringLiteral("m_data"), QStringLiteral("")}); QList> path2Children; path2Children.append({QStringLiteral("m_data"), QStringLiteral("")}); VERIFY_WATCH(0, "path1", "(\"tmp\", \"foo\")", path1Children); VERIFY_WATCH(1, "path2", "(\"http://www.test.com\", \"tmp\", \"asdf.txt\")", path2Children); } QTEST_MAIN(LldbFormattersTest) #include "test_lldbformatters.moc" diff --git a/plugins/makebuilder/kdevmakebuilder.json b/plugins/makebuilder/kdevmakebuilder.json index 4a04be907d..7572ba8262 100644 --- a/plugins/makebuilder/kdevmakebuilder.json +++ b/plugins/makebuilder/kdevmakebuilder.json @@ -1,67 +1,63 @@ { "KPlugin": { "Category": "Project Management", "Description": "KDevelop Make Builder", - "Description[bs]": "KDevelop Make izgraditelj", "Description[ca@valencia]": "Constructor Make del KDevelop", "Description[ca]": "Constructor Make del KDevelop", "Description[de]": "Make-Builder von KDevelop", - "Description[el]": "Κατασκευαστής make του KDevelop", "Description[en_GB]": "KDevelop Make Builder", "Description[es]": "Compilador Make para KDevelop", "Description[et]": "KDevelopi Make'i ehitaja", "Description[fi]": "KDevelop Make-käännin", "Description[fr]": "Constructeur Make de KDevelop", "Description[gl]": "Contrutor de Make para KDevelop.", "Description[it]": "Compilatore Make di KDevelop", "Description[nl]": "KDevelop Make-bouwer", "Description[pl]": "Budowniczy Make", "Description[pt]": "Construtor de Projectos Make no KDevelop", "Description[pt_BR]": "Compilador do Make para o KDevelop", "Description[sk]": "Prekladač KDevelop Make", "Description[sl]": "Izgrajevalnik Make za KDevelop", "Description[sv]": "KDevelop byggverktyg för Make", "Description[tr]": "KDevelop Make İnşa Edici", "Description[uk]": "Збирач Make для KDevelop", "Description[x-test]": "xxKDevelop Make Builderxx", "Description[zh_CN]": "KDevelop Make 构建器", "Icon": "kdevelop", "Id": "KDevMakeBuilder", "Name": "Make Project Builder", - "Name[bs]": "Napravi graditelja projekta", "Name[ca@valencia]": "Constructor de projecte Make", "Name[ca]": "Constructor de projecte Make", "Name[cs]": "Překladač projektů Make", "Name[de]": "Ersteller für Make-Projekte", - "Name[el]": "Κατασκευαστής έργου make", "Name[en_GB]": "Make Project Builder", "Name[es]": "Compilador de proyectos Make", "Name[et]": "Make'i projektiehitaja", "Name[fi]": "Make-projektikäännin", "Name[fr]": "Constructeur de projet Make", "Name[gl]": "Construtor de proxectos Make", "Name[it]": "Compilatore progetto Make", "Name[nl]": "Make-projectbouwer", "Name[pl]": "Budowniczy projektu Make", - "Name[pt]": "Compilador de Projectos do Make", + "Name[pt]": "Compilador de Projectos Make", "Name[pt_BR]": "Compilador de projeto do Make", "Name[sk]": "Prekladač projektov Make", "Name[sl]": "Izgrajevalnik projektov Make", "Name[sv]": "Make-projektbyggverktyg", "Name[tr]": "Make Proje Oluşturucu", "Name[uk]": "Інструмент збирання проєктів Make", "Name[x-test]": "xxMake Project Builderxx", "Name[zh_CN]": "Make 工程构建器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IMakeBuilder" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/manpage/kdevmanpage.json b/plugins/manpage/kdevmanpage.json index ac02df2c1b..499bd18880 100644 --- a/plugins/manpage/kdevmanpage.json +++ b/plugins/manpage/kdevmanpage.json @@ -1,66 +1,62 @@ { "KPlugin": { "Category": "Documentation", "Description": "This plugin provides Manual Pages integration", - "Description[bs]": "Ovaj dodatak omogućava integraciju sa stranicama uputstava", "Description[ca@valencia]": "Aquest connector proporciona la integració de les pàgines del manual", "Description[ca]": "Aquest connector proporciona la integració de les pàgines del manual", "Description[de]": "Dieses Modul integriert Handbuch-Seiten", - "Description[el]": "Αυτό το πρόσθετο παρέχει ενσωμάτωση των σελίδων του εγχειριδίου", "Description[en_GB]": "This plugin provides Manual Pages integration", "Description[es]": "Este complemento proporciona la integración de páginas de manual", - "Description[et]": "See plugin võimaldab manuaalilehekülgede lõimimist", + "Description[et]": "See plugin pakub manuaalilehekülgede lõimimist", "Description[fi]": "Tämä liitännäinen tarjoaa integraation manuaalisivuille", "Description[fr]": "Ce module fournit une intégration des pages de manuel", "Description[gl]": "Este complemento fornece integración coas páxinas de manual.", "Description[it]": "Questa estensione fornisce l'integrazione delle pagine del manuale", "Description[nl]": "Deze plugin biedt integratie met man-pagina's", "Description[pl]": "Wplata strony podręczników", "Description[pt]": "Este 'plugin' oferece a integração com as páginas de manual do Man", "Description[pt_BR]": "Este plugin fornece integração com as páginas de manuais", "Description[sk]": "Tento modul poskytuje integráciu manuálových stránok", "Description[sl]": "Ta vstavek omogoči podporo za strani man", "Description[sv]": "Insticksprogrammet tillhandahåller integrering av manualsidor", "Description[tr]": "Bu eklenti Kılavuz Sayfaları bütünleşmesini sağlar", "Description[uk]": "За допомогою цього додатка здійснюється інтеграція з системою сторінок підручника (man)", "Description[x-test]": "xxThis plugin provides Manual Pages integrationxx", "Description[zh_CN]": "此插件提供了帮助手册整合", "EnabledByDefault": false, "Icon": "x-office-address-book", "Id": "KDevManPage", "License": "GPL", "Name": "Man Pages", - "Name[bs]": "Man stgranice", "Name[ca@valencia]": "Pàgines «man»", "Name[ca]": "Pàgines «man»", "Name[cs]": "Manuálové stránky", "Name[de]": "Handbuch-Seiten", - "Name[el]": "Σελίδες εγχειριδίου", "Name[en_GB]": "Man Pages", "Name[es]": "Páginas de manual", - "Name[et]": "Manuaalileheküljed", + "Name[et]": "Man-leheküljed", "Name[fi]": "Mansivut", "Name[fr]": "Pages manuel", "Name[gl]": "Páxinas de manuais", "Name[it]": "Pagine man", "Name[nl]": "Man-pagina's", "Name[pl]": "Strony instrukcji", "Name[pt]": "Páginas do Man", "Name[pt_BR]": "Páginas de manuais", "Name[sk]": "Manuálové stránky", "Name[sl]": "Strani man", "Name[sv]": "Manualsidor", "Name[tr]": "Kılavuz Sayfaları", "Name[uk]": "Сторінки man", "Name[x-test]": "xxMan Pagesxx", "Name[zh_CN]": "Man 手册", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IDocumentationProvider" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/meson/kdevmesonmanager.json b/plugins/meson/kdevmesonmanager.json index def8e287e8..d9435032cd 100644 --- a/plugins/meson/kdevmesonmanager.json +++ b/plugins/meson/kdevmesonmanager.json @@ -1,66 +1,60 @@ { "KPlugin": { "Category": "Project Management", "Description": "Imports and edits Meson projects", - "Description[ca@valencia]": "Importa i edita projectes del Meson", "Description[ca]": "Importa i edita projectes del Meson", "Description[cs]": "Importuje a upravuje vlastní projekty Meson", "Description[de]": "Import und Bearbeitung von benutzerdefinierten Meson-Projekten", - "Description[el]": "Εισάγει και επεξεργάζεται έργά meson", "Description[en_GB]": "Imports and edits Meson projects", "Description[es]": "Importa y edita proyectos Meson", - "Description[et]": "Mesoni projektide importimine ja muutmine", "Description[fr]": "Importe et édite des projets Meson", "Description[gl]": "Importa e edita proxectos Meson", "Description[it]": "Importa e modifica i progetti Meson", "Description[nl]": "Importeert en bewerkt Meson-projecten", "Description[pl]": "Importuje i edytuje projekty Meson", "Description[pt]": "Importa e edita os projectos do Meson", "Description[pt_BR]": "Importa e edita os projetos do Meson", "Description[sk]": "Importuje a upravuje projekty Meson", "Description[sv]": "Importerar och redigerar Meson-projekt", "Description[uk]": "Імпортує і дає змогу редагувати проєкти Meson", "Description[x-test]": "xxImports and edits Meson projectsxx", "Description[zh_CN]": "倒入并编辑 Meson 工程", "Icon": "run-build", "Id": "KDevMesonManager", "Name": "Meson Project Manager", - "Name[ca@valencia]": "Gestor de projectes Meson", "Name[ca]": "Gestor de projectes Meson", "Name[cs]": "Správce projektů Meson", "Name[de]": "Meson-Projektverwaltung", - "Name[el]": "Διαχειριστής έργου meson", "Name[en_GB]": "Meson Project Manager", "Name[es]": "Gestor de proyectos Meson", - "Name[et]": "Mesoni projektihaldur", "Name[fr]": "Gestionnaire de projet Meson", "Name[gl]": "Xestor de proxectos Meson", "Name[it]": "Gestore progetto Meson", - "Name[nl]": "Meson projectbeheerder", + "Name[nl]": "Meson-projectbeheerder", "Name[pl]": "Zarządzanie projektami Meson", - "Name[pt]": "Gestor de Projectos do Meson", + "Name[pt]": "Gestor de Projectos Meson", "Name[pt_BR]": "Gerenciador de projetos do Meson", "Name[sk]": "Správca projektov Meson", - "Name[sv]": "Meson projekthanterare", + "Name[sv]": "Meson-projekthantering", "Name[uk]": "Керування проєктами Meson", "Name[x-test]": "xxMeson Project Managerxx", "Name[zh_CN]": "Meson 工程管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-FileManager": "Meson", "X-KDevelop-IRequired": [ "org.kdevelop.IProjectBuilder" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBuildSystemManager", "org.kdevelop.IProjectFileManager" ], "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectFilesFilter": [ "meson.build" ], "X-KDevelop-ProjectFilesFilterDescription": "Meson Project Files" } diff --git a/plugins/ninjabuilder/kdevninja.json b/plugins/ninjabuilder/kdevninja.json index f3f91f6d54..1be8524461 100644 --- a/plugins/ninjabuilder/kdevninja.json +++ b/plugins/ninjabuilder/kdevninja.json @@ -1,65 +1,63 @@ { "KPlugin": { "Category": "Project Management", "Description": "KDevelop Ninja Builder", "Description[ca@valencia]": "Constructor Ninja del KDevelop", "Description[ca]": "Constructor Ninja del KDevelop", "Description[de]": "KDevelop-Ninja-Erstellung", - "Description[el]": "Κατασκευαστής ninja του KDevelop", "Description[en_GB]": "KDevelop Ninja Builder", "Description[es]": "Compilador Ninja para KDevelop", "Description[et]": "KDevelopi Ninja ehitaja", "Description[fi]": "KDevelop Ninja-käännin", "Description[fr]": "Constructeur Ninja de KDevelop", "Description[gl]": "Construtor de Ninja para KDevelop.", "Description[it]": "Compilatore Ninja di KDevelop", "Description[nl]": "KDevelop Ninja-bouwprogramma", "Description[pl]": "Budowniczy Ninja", "Description[pt]": "Construtor de Projectos Ninja no KDevelop", "Description[pt_BR]": "Compilador Ninja do KDevelop", "Description[sk]": "Zostavovač KDevelop Ninja", "Description[sl]": "Izgrajevalnik Ninja za KDevelop", "Description[sv]": "KDevelop byggverktyg för Ninja", "Description[tr]": "KDevelop Ninja Oluşturucu", "Description[uk]": "Збирач Ninja для KDevelop", "Description[x-test]": "xxKDevelop Ninja Builderxx", "Description[zh_CN]": "KDevelop Ninja 构建器", "Icon": "kdevelop", "Id": "KDevNinjaBuilder", "Name": "Ninja Project Builder", "Name[ca@valencia]": "Constructor de projectes Ninja", "Name[ca]": "Constructor de projectes Ninja", "Name[cs]": "Překladač projektů Ninja", "Name[de]": "Ninja-Projekterstellung", - "Name[el]": "Κατασκευαστής έργου ninja", "Name[en_GB]": "Ninja Project Builder", "Name[es]": "Compilador de proyectos Ninja", "Name[et]": "Ninja projektiehitaja", "Name[fi]": "Ninja-projektikäännin", "Name[fr]": "Constructeur de projet Ninja", "Name[gl]": "Construtor de proxectos Ninja", "Name[it]": "Compilatore progetto Ninja", "Name[nl]": "Ninja-project bouwprogramma", "Name[pl]": "Budowniczy projektu Ninja", - "Name[pt]": "Compilador de Projectos do Ninja", + "Name[pt]": "Compilador de Projectos Ninja", "Name[pt_BR]": "Compilador de projetos Ninja", "Name[sk]": "Zostavovač projektov Ninja", "Name[sl]": "Izgrajevalnik projektov Ninja", "Name[sv]": "Ninja projektbyggverktyg", "Name[tr]": "Ninja Proje Oluşturucu", "Name[uk]": "Збирач проєктів Ninja", "Name[x-test]": "xxNinja Project Builderxx", "Name[zh_CN]": "Ninja 工程构建器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectBuilder" ], "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/okteta/kdevokteta.json b/plugins/okteta/kdevokteta.json index bdbe274ce1..7a68468ec1 100644 --- a/plugins/okteta/kdevokteta.json +++ b/plugins/okteta/kdevokteta.json @@ -1,90 +1,87 @@ { "KPlugin": { "Authors": [ { "Email": "kossebau@kde.org", "Name": "Friedrich W. H. Kossebau", "Name[ca@valencia]": "Friedrich W. H. Kossebau", "Name[ca]": "Friedrich W. H. Kossebau", "Name[cs]": "Friedrich W. H. Kossebau", "Name[de]": "Friedrich W. H. Kossebau", - "Name[el]": "Friedrich W. H. Kossebau", "Name[en_GB]": "Friedrich W. H. Kossebau", "Name[es]": "Friedrich W. H. Kossebau", "Name[et]": "Friedrich W. H. Kossebau", "Name[fi]": "Friedrich W. H. Kossebau", "Name[fr]": "Friedrich W. H. Kossebau", "Name[gl]": "Friedrich W. H. Kossebau", "Name[it]": "Friedrich W. H. Kossebau", "Name[nl]": "Friedrich W. H. Kossebau", "Name[nn]": "Friedrich W.H. Kossebau", "Name[pl]": "Friedrich W. H. Kossebau", "Name[pt]": "Friedrich W. H. Kossebau", "Name[pt_BR]": "Friedrich W. H. Kossebau", "Name[ru]": "Friedrich W. H. Kossebau", "Name[sk]": "Friedrich W. H. Kossebau", "Name[sl]": "Friedrich W. H. Kossebau", "Name[sv]": "Friedrich W. H. Kossebau", "Name[tr]": "Friedrich W. H. Kossebau", "Name[uk]": "Friedrich W. H. Kossebau", "Name[x-test]": "xxFriedrich W. H. Kossebauxx", "Name[zh_CN]": "Friedrich W. H. Kossebau" } ], "Category": "Utilities", "Description": "Enables viewing and editing of the raw data of files (Okteta-based)", "Description[ca@valencia]": "Permet la visualització i edició de les dades sense processar dels fitxers (basat en l'Okteta)", "Description[ca]": "Permet la visualització i edició de les dades sense processar dels fitxers (basat en l'Okteta)", "Description[de]": "Ermöglicht das Betrachten und Bearbeiten der Rohdaten von Dateien (basiert auf Okteta)", - "Description[el]": "Ενεργοποιεί την προβολή και επεξεργασία των ακατέργαστων δεδομένων αρχείων (με βάση το octeta)", "Description[en_GB]": "Enables viewing and editing of the raw data of files (Okteta-based)", "Description[es]": "Activa la visualización y la edición de datos de archivos en bruto (basado en Okteta)", "Description[et]": "Võimaldab näha ja muuta failide toorandmeid (põhineb Oktetal)", "Description[fr]": "Permet l'aperçu et l'édition des données brutes de fichiers (repose sur Okteta)", "Description[gl]": "Permite visualizar e editar os datos en bruto de ficheiros (baseado en Okteta).", "Description[it]": "Abilita la visualizzazione e la modifica dei dati grezzi dei file (basato su Okteta)", "Description[nl]": "Schakelt het bekijken en bewerken van de ruwe gegevens van bestanden in (gebaseerd op Okteta)", "Description[pl]": "Umożliwia oglądanie i edytowanie nieprzetworzonych danych pliku (przy użyciu Oktety)", - "Description[pt]": "Permite a visualização e edição dos dados em bruto dos ficheiros (baseado no Okteta)", + "Description[pt]": "Activa a visualização e a edição dos dados em bruto dos ficheiros (baseado no Okteta)", "Description[pt_BR]": "Ativa a visualização e edição dos dados brutos dos arquivos (baseado no Okteta)", "Description[sk]": "Povolí prezeranie a editáciu raw dát súborov (založené na Okteta)", "Description[sl]": "Omogoča pregledovanje in urejanje surovih podatkov datotek (temelji na Okteti)", "Description[sv]": "Möjliggör granskning och redigering av rådata i filer (baserad på Okteta)", "Description[tr]": "Dosyaların ham verisini okuma ve düzenlemeyi sağlar (Oktet-tabanlı)", "Description[uk]": "Уможливлює перегляд і редагування необроблених даних у файлах (на основі Okteta)", "Description[x-test]": "xxEnables viewing and editing of the raw data of files (Okteta-based)xx", "Description[zh_CN]": "允许查看和编辑文件的原始数据 (基于 Okteta)", "Icon": "okteta", "Id": "kdevokteta", "License": "GPL", "Name": "Hex Editor", "Name[ca@valencia]": "Editor hexadecimal", "Name[ca]": "Editor hexadecimal", "Name[cs]": "Hexa editor", "Name[de]": "Hex-Editor", - "Name[el]": "Επεξεργαστής δεκαεξαδικών", "Name[en_GB]": "Hex Editor", "Name[es]": "Editor hexadecimal", "Name[et]": "Binaarfailide redaktor", "Name[fr]": "Éditeur hexadécimal", "Name[gl]": "Editor de binario", "Name[it]": "Editor esadecimale", "Name[nl]": "Hexbewerker", "Name[pl]": "Edytor szesnastkowy", "Name[pt]": "Editor Hexadecimal", "Name[pt_BR]": "Editor hexadecimal", "Name[sk]": "Hex editor", "Name[sl]": "Šestnajstiški urejevalnik", "Name[sv]": "Hexadecimaleditor", "Name[tr]": "Hex Düzenleyici", "Name[uk]": "Шістнадцятковий редактор", "Name[x-test]": "xxHex Editorxx", "Name[zh_CN]": "十六进制编辑器", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.1.0" }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/openwith/kdevopenwith.json b/plugins/openwith/kdevopenwith.json index 85da0f9581..9bec968100 100644 --- a/plugins/openwith/kdevopenwith.json +++ b/plugins/openwith/kdevopenwith.json @@ -1,103 +1,90 @@ { "KPlugin": { "Authors": [ { "Name": "Andreas Pakulat", "Name[ca@valencia]": "Andreas Pakulat", "Name[ca]": "Andreas Pakulat", "Name[cs]": "Andreas Pakulat", "Name[de]": "Andreas Pakulat", - "Name[el]": "Andreas Pakulat", "Name[en_GB]": "Andreas Pakulat", "Name[es]": "Andreas Pakulat", "Name[et]": "Andreas Pakulat", "Name[fr]": "Andreas Pakulat", "Name[gl]": "Andreas Pakulat", "Name[it]": "Andreas Pakulat", "Name[nl]": "Andreas Pakulat", "Name[nn]": "Andreas Pakulat", "Name[pl]": "Andreas Pakulat", "Name[pt]": "Andreas Pakulat", "Name[pt_BR]": "Andreas Pakulat", "Name[ru]": "Andreas Pakulat", "Name[sk]": "Andreas Pakulat", "Name[sl]": "Andreas Pakulat", "Name[sv]": "Andreas Pakulat", "Name[tr]": "Andreas Pakulat", "Name[uk]": "Andreas Pakulat", "Name[x-test]": "xxAndreas Pakulatxx", "Name[zh_CN]": "Andreas Pakulat" } ], "Category": "Core", "Description": "This plugin allows one to open files with associated external applications.", "Description[ca@valencia]": "Aquest connector permet obrir fitxers amb les aplicacions externes associades.", "Description[ca]": "Aquest connector permet obrir fitxers amb les aplicacions externes associades.", "Description[cs]": "Tento modul umožňuje otevření souborů přiřazenými externími aplikacemi.", "Description[de]": "Mit diesem Modul können Dateien mit ihnen zugewiesenen externen Anwendungen gestartet werden.", - "Description[el]": "Αυτό το πρόσθετο επιτρέπει το άνοιγμα αρχείων με σχετικές εξωτερικές εφαρμογές", "Description[en_GB]": "This plugin allows one to open files with associated external applications.", "Description[es]": "Este complemento permite abrir archivos con las aplicaciones externas asociadas.", "Description[et]": "See plugin võimaldab avada väliste rakendustega seostatud faile.", "Description[fr]": "Ce module permet d'ouvrir des fichiers en association avec des applications externes.", "Description[gl]": "Este complemento permítelle abrir ficheiros coas aplicacións externas asociadas.", "Description[it]": "Questa estensione permette di aprire i file con le relative applicazioni esterne.", "Description[nl]": "Deze plugin biedt u het openen van bestanden met geassocieerde externe toepassingen.", "Description[pl]": "Otwiera pliki w zewnętrznych programach.", "Description[pt]": "Este 'plugin' permite abrir os ficheiros com as aplicações externas associadas.", "Description[pt_BR]": "Este plugin permite abrir os arquivos com os aplicativos externos associados.", "Description[sk]": "Tento plugin umožňuje otvoriť súbory s asociovanými externými aplikáciami.", "Description[sl]": "Vstavek omogoča odpiranje datotek v povezanih zunanjih programih.", "Description[sv]": "Insticksprogrammet tillåter att öppna filer med tillhörande externa program.", "Description[tr]": "Bu eklenti ilişkilendirilmiş dış uygulamalar ile dosyalar açmayı sağlar.", "Description[uk]": "За допомогою цього додатка ви зможете відкривати файли у пов’язаній з ними зовнішній програмі.", "Description[x-test]": "xxThis plugin allows one to open files with associated external applications.xx", "Description[zh_CN]": "此插件允许使用关联的外部应用程序打开文件。", "Icon": "document-open", "Id": "kdevopenwith", "License": "GPL", "Name": "Open With", - "Name[ar]": "افتح بِـ", - "Name[bg]": "Отваряне с", - "Name[bs]": "Otvori pomoću", "Name[ca@valencia]": "Obri amb", "Name[ca]": "Obre amb", "Name[cs]": "Otevřít pomocí", - "Name[da]": "Åbn med", "Name[de]": "Öffnen mit", - "Name[el]": "Άνοιγμα με", "Name[en_GB]": "Open With", "Name[es]": "Abrir con", "Name[et]": "Avamine rakendusega", "Name[fr]": "Ouvrir avec", "Name[gl]": "Abrir con", - "Name[hu]": "Megnyitás ezzel", "Name[it]": "Apri con", - "Name[kk]": "Мынамен ашу", - "Name[mr]": "यामध्ये उघडा", "Name[nb]": "Åpne med", - "Name[nds]": "Opmaken mit", "Name[nl]": "Openen met", "Name[pl]": "Otwórz za pomocą", "Name[pt]": "Abrir Com", "Name[pt_BR]": "Abrir com", "Name[ru]": "Открыть с помощью", "Name[sk]": "Otvoriť s", "Name[sl]": "Odpri z", "Name[sv]": "Öppna med", "Name[tr]": "Birlikte Aç", - "Name[ug]": "بۇنىڭدا ئېچىش", "Name[uk]": "Відкриття у зовнішніх програмах", "Name[x-test]": "xxOpen Withxx", "Name[zh_CN]": "打开方式", - "Name[zh_TW]": "開啟方式", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IOpenWith" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/outlineview/kdevoutlineview.json b/plugins/outlineview/kdevoutlineview.json index ffdff665aa..84c02ab1f5 100644 --- a/plugins/outlineview/kdevoutlineview.json +++ b/plugins/outlineview/kdevoutlineview.json @@ -1,88 +1,85 @@ { "KPlugin": { "Authors": [ { "Email": "arichardson.kde@gmail.com", "Name": "Alex Richardson", "Name[ca@valencia]": "Alex Richardson", "Name[ca]": "Alex Richardson", "Name[cs]": "Alex Richardson", "Name[de]": "Alex Richardson", - "Name[el]": "Alex Richardson", "Name[en_GB]": "Alex Richardson", "Name[es]": "Alex Richardson", "Name[et]": "Alex Richardson", "Name[fr]": "Alex Richardson", "Name[gl]": "Alex Richardson", "Name[it]": "Alex Richardson", "Name[nl]": "Alex Richardson", "Name[nn]": "Alex Richardson", "Name[pl]": "Alex Richardson", "Name[pt]": "Alex Richardson", "Name[pt_BR]": "Alex Richardson", "Name[ru]": "Alex Richardson", "Name[sk]": "Alex Richardson", "Name[sl]": "Alex Richardson", "Name[sv]": "Alex Richardson", "Name[tr]": "Alex Richardson", "Name[uk]": "Alex Richardson", "Name[x-test]": "xxAlex Richardsonxx", "Name[zh_CN]": "Alex Richardson" } ], "Category": "Core", "Description": "This plugin provides a view to show the outline of the currently open document.", "Description[ca@valencia]": "Aquest connector proporciona una visualització que mostra una visió general del document obert actualment.", "Description[ca]": "Aquest connector proporciona una visualització que mostra una visió general del document obert actualment.", "Description[de]": "Dieses Modul zeigt eine Ansicht der Funktions-Kurzinfos im aktuell geöffneten Dokument.", - "Description[el]": "Αυτό το πρόσθετο προσφέρει μια προβολή εμφάνισης του περιγράμματος του τρέχοντος ανοιχτού εγγράφου.", "Description[en_GB]": "This plugin provides a view to show the outline of the currently open document.", "Description[es]": "Este complemento proporciona una vista para mostrar el boceto del documento actualmente abierto.", - "Description[et]": "See plugin pakub parajasti avatud dokumendi struktuurivaadet.", + "Description[et]": "See plugin pakub parajasti avatud dokumendi struktuuri vaadet.", "Description[fr]": "Ce module fournit une vue pour afficher le plan du document ouvert", "Description[gl]": "Este complemento fornece unha vista para mostrar o esquema do documento actual.", "Description[it]": "Questa estensione fornisce una vista che mostra uno schema di massima del documento attualmente aperto.", "Description[nl]": "Deze plugin biedt zicht op de outline van het nu geopende document.", "Description[pl]": "Umożliwia przeglądanie zarysu obecnie otwartego dokumentu.", "Description[pt]": "Este 'plugin' oferece uma visão geral sobre o documento aberto de momento.", "Description[pt_BR]": "Este plugin fornece uma visão geral do documento que estiver aberto.", "Description[sk]": "Tento plugin poskytuje pohľad na zobrazenie obrysu aktuálne otvoreného dokumentu.", "Description[sl]": "Ta vstavek ponuja prikaz za oris trenutno odprtega dokumenta.", "Description[sv]": "Insticksprogrammet tillhandahåller en vy för att visa dispositionen av dokumentet som för närvarande är öppet.", "Description[tr]": "Bu eklenti mevcut açık dosyanın çerçevesini görüntülemeyi sağlar.", "Description[uk]": "За допомогою цього додатка можна переглянути схему поточного відкритого документа.", "Description[x-test]": "xxThis plugin provides a view to show the outline of the currently open document.xx", "Description[zh_CN]": "此插件提供了一个当前显示当前打开文档的概要的视图。", "Icon": "code-class", "Id": "KDevOutlineView", "License": "LGPL", "Name": "Outline", "Name[ca@valencia]": "Visió general", "Name[ca]": "Visió general", "Name[cs]": "Obrys", "Name[de]": "Funktions-Kurzinfo", - "Name[el]": "Outline", "Name[en_GB]": "Outline", "Name[es]": "Boceto", "Name[et]": "Struktuur", "Name[fr]": "Plan", "Name[gl]": "Esquema", "Name[it]": "Schema di massima", "Name[nl]": "Outline", "Name[pl]": "Zarys", "Name[pt]": "Visão Geral", "Name[pt_BR]": "Contorno", "Name[sk]": "Obrys", "Name[sl]": "Oris", "Name[sv]": "Disposition", "Name[tr]": "Çerçeve", "Name[uk]": "Схема", "Name[x-test]": "xxOutlinexx", "Name[zh_CN]": "轮廓", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/patchreview/kdevpatchreview.json b/plugins/patchreview/kdevpatchreview.json index fe066f6e5e..b0c080a119 100644 --- a/plugins/patchreview/kdevpatchreview.json +++ b/plugins/patchreview/kdevpatchreview.json @@ -1,112 +1,98 @@ { "KPlugin": { "Authors": [ { "Name": "David Nolden", "Name[ca@valencia]": "David Nolden", "Name[ca]": "David Nolden", "Name[cs]": "David Nolden", "Name[de]": "David Nolden", - "Name[el]": "David Nolden", "Name[en_GB]": "David Nolden", "Name[es]": "David Nolden", "Name[et]": "David Nolden", "Name[fi]": "David Nolden", "Name[fr]": "David Nolden", "Name[gl]": "David Nolden", "Name[it]": "David Nolden", "Name[nl]": "David Nolden", "Name[nn]": "David Nolden", "Name[pl]": "David Nolden", "Name[pt]": "David Nolden", "Name[pt_BR]": "David Nolden", "Name[ru]": "David Nolden", "Name[sk]": "David Nolden", "Name[sl]": "David Nolden", "Name[sv]": "David Nolden", "Name[tr]": "David Nolden", "Name[uk]": "David Nolden", "Name[x-test]": "xxDavid Noldenxx", "Name[zh_CN]": "David Nolden" } ], "Category": "Utilities", "Description": "This plugin allows reviewing patches directly in the editor.", - "Description[ar]": "تسمح هذه الملحقة بمراجعة الرّقعة مباشرةً في المحرّر.", "Description[ca@valencia]": "Aquest connector permet revisar pedaços directament en l'editor.", "Description[ca]": "Aquest connector permet revisar pedaços directament en l'editor.", "Description[cs]": "Tento zásuvný modul umožňuje prohlížení záplat přímo v editoru.", "Description[de]": "Dieses Modul ermöglicht es, Patches direkt im Editor durchzusehen.", - "Description[el]": "Αυτό το πρόσθετο σάς επιτρέπει να επιθεωρείτε διορθώσεις απευθείας από τον κειμενογράφο.", "Description[en_GB]": "This plugin allows reviewing patches directly in the editor.", "Description[es]": "Este complemento permite la revisión de parches directamente en el editor.", "Description[et]": "See plugin võimaldab paiku üle vaadata otse redaktoris.", "Description[fr]": "Ce module permet de passer en revue des correctifs directement dans l'éditeur.", "Description[gl]": "Este complemento permite revisar parches directamente no editor.", "Description[it]": "Questa estensione permette di rivedere le patch direttamente nell'editor.", "Description[nl]": "Deze plugin laat patches herzien direct in de editor.", "Description[pl]": "Umożliwia przeglądanie łatek bezpośrednio w edytorze.", "Description[pt]": "Este 'plugin' permite a revisão das modificações directamente no editor.", "Description[pt_BR]": "Este plugin permite obter as modificações diretamente no editor.", "Description[sk]": "Tento plugin umožňuje zhodnotenie záplat priamo v editore.", "Description[sl]": "Vstavek omogoča pregled popravkov neposredno v urejevalniku.", "Description[sv]": "Insticksprogrammet gör det möjligt att direkt granska programfixar i editorn.", "Description[tr]": "Bu eklenti yamaların doğrudan düzenleyici içerisinde gözden geçirilmesini sağlar.", "Description[uk]": "За допомогою цього додатка ви зможете рецензувати латки безпосередньо у редакторі.", "Description[x-test]": "xxThis plugin allows reviewing patches directly in the editor.xx", "Description[zh_CN]": "此插件允许在编辑器中直接审阅补丁。", - "Description[zh_TW]": "此外掛程式任您可以直接在編輯器裡檢視修補。", "Icon": "text-x-patch", "Id": "kdevpatchreview", "License": "GPL", "Name": "Patch Review", - "Name[ar]": "مراجعة الرّقع", - "Name[bg]": "Преглед на кръпки", - "Name[bs]": "Pregled zakrpa", "Name[ca@valencia]": "Revisió del pedaç", "Name[ca]": "Revisió del pedaç", "Name[cs]": "Kontrola záplat", - "Name[da]": "Gennemgang af rettelser", "Name[de]": "Patch-Durchsicht", - "Name[el]": "Επιθεώρηση διορθώσεων", "Name[en_GB]": "Patch Review", "Name[es]": "Revisión de parches", - "Name[et]": "Paikade ülevaatamine", + "Name[et]": "Paikade läbivaatus", "Name[fr]": "Révision de correctifs", "Name[gl]": "Revisor de parches", - "Name[hu]": "Javítócsomag átnézés", "Name[it]": "Revisione patch", - "Name[kk]": "Жамау шолуы", "Name[nb]": "Lappegjennomgang", - "Name[nds]": "Plasternakiek", "Name[nl]": "Patchoverzicht", - "Name[pl]": "Przeglądanie poprawek", + "Name[pl]": "Ocena łatki", "Name[pt]": "Revisão da Modificação", - "Name[pt_BR]": "Revisão da Modificação", + "Name[pt_BR]": "Revisão da modificação", "Name[ru]": "Рецензирование патчей", "Name[sk]": "Zhodnotenie záplaty", "Name[sl]": "Pregled popravkov", "Name[sv]": "Granska programfixar", "Name[tr]": "Yama Gözden Geçirmesi", - "Name[ug]": "ياماق باھالاش", "Name[uk]": "Рецензування латки", "Name[x-test]": "xxPatch Reviewxx", "Name[zh_CN]": "补丁审阅", - "Name[zh_TW]": "修補檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IPatchReview", "ILanguageSupport" ], "X-KDevelop-Languages": [ "Diff" ], "X-KDevelop-Mode": "GUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-diff", "text/x-patch" ] } diff --git a/plugins/patchreview/patchreviewtoolview.cpp b/plugins/patchreview/patchreviewtoolview.cpp index f06810e41e..8fbb5b88ee 100644 --- a/plugins/patchreview/patchreviewtoolview.cpp +++ b/plugins/patchreview/patchreviewtoolview.cpp @@ -1,603 +1,607 @@ /*************************************************************************** Copyright 2006-2009 David Nolden ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "patchreviewtoolview.h" #include "localpatchsource.h" #include "patchreview.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_PURPOSE #include #include #endif using namespace KDevelop; class PatchFilesModel : public VcsFileChangesModel { Q_OBJECT public: PatchFilesModel( QObject *parent, bool allowSelection ) : VcsFileChangesModel( parent, allowSelection ) { }; enum ItemRoles { HunksNumberRole = LastItemRole+1 }; public Q_SLOTS: void updateState( const KDevelop::VcsStatusInfo &status, unsigned hunksNum ) { int row = VcsFileChangesModel::updateState( invisibleRootItem(), status ); if ( row == -1 ) return; QStandardItem *item = invisibleRootItem()->child( row, 0 ); setFileInfo( item, hunksNum ); item->setData( QVariant( hunksNum ), HunksNumberRole ); } void updateState( const KDevelop::VcsStatusInfo &status ) { int row = VcsFileChangesModel::updateState( invisibleRootItem(), status ); if ( row == -1 ) return; QStandardItem *item = invisibleRootItem()->child( row, 0 ); setFileInfo( invisibleRootItem()->child( row, 0 ), item->data( HunksNumberRole ).toUInt() ); } private: void setFileInfo( QStandardItem *item, unsigned int hunksNum ) { const auto url = item->index().data(VcsFileChangesModel::UrlRole).toUrl(); const QString path = ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain); const QString newText = i18ncp( "%1: number of changed hunks, %2: file name", "%2 (1 hunk)", "%2 (%1 hunks)", hunksNum, path); item->setText( newText ); } }; PatchReviewToolView::PatchReviewToolView( QWidget* parent, PatchReviewPlugin* plugin ) : QWidget( parent ), m_resetCheckedUrls( true ), m_plugin( plugin ) { setWindowIcon(QIcon::fromTheme(QStringLiteral("text-x-patch"), windowIcon())); connect( m_plugin->finishReviewAction(), &QAction::triggered, this, &PatchReviewToolView::finishReview ); connect( plugin, &PatchReviewPlugin::patchChanged, this, &PatchReviewToolView::patchChanged ); connect( plugin, &PatchReviewPlugin::startingNewReview, this, &PatchReviewToolView::startingNewReview ); connect( ICore::self()->documentController(), &IDocumentController::documentActivated, this, &PatchReviewToolView::documentActivated ); auto* w = qobject_cast(ICore::self()->uiController()->activeMainWindow()); connect(w, &Sublime::MainWindow::areaChanged, m_plugin, &PatchReviewPlugin::areaChanged); showEditDialog(); patchChanged(); } void PatchReviewToolView::resizeEvent(QResizeEvent* ev) { bool vertical = (width() < height()); m_editPatch.buttonsLayout->setDirection(vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); m_editPatch.contentLayout->setDirection(vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); m_editPatch.buttonsSpacer->changeSize(vertical ? 0 : 40, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget::resizeEvent(ev); if(m_customWidget) { m_editPatch.contentLayout->removeWidget( m_customWidget ); m_editPatch.contentLayout->insertWidget(0, m_customWidget ); } } void PatchReviewToolView::startingNewReview() { m_resetCheckedUrls = true; } void PatchReviewToolView::patchChanged() { fillEditFromPatch(); kompareModelChanged(); #ifdef WITH_PURPOSE IPatchSource::Ptr p = m_plugin->patch(); if (p) { m_exportMenu->model()->setInputData(QJsonObject { { QStringLiteral("urls"), QJsonArray { p->file().toString() } }, { QStringLiteral("mimeType"), { QStringLiteral("text/x-patch") } }, { QStringLiteral("localBaseDir"), { p->baseDir().toString() } }, { QStringLiteral("updateComment"), { QStringLiteral("Patch updated through KDevelop's Patch Review plugin") } } }); } #endif } PatchReviewToolView::~PatchReviewToolView() { } LocalPatchSource* PatchReviewToolView::GetLocalPatchSource() { IPatchSource::Ptr ips = m_plugin->patch(); if ( !ips ) return nullptr; return qobject_cast(ips.data()); } void PatchReviewToolView::fillEditFromPatch() { IPatchSource::Ptr ipatch = m_plugin->patch(); if ( !ipatch ) return; m_editPatch.cancelReview->setVisible( ipatch->canCancel() ); m_fileModel->setIsCheckbable( m_plugin->patch()->canSelectFiles() ); if( m_customWidget ) { qCDebug(PLUGIN_PATCHREVIEW) << "removing custom widget"; m_customWidget->hide(); m_editPatch.contentLayout->removeWidget( m_customWidget ); } m_customWidget = ipatch->customWidget(); if( m_customWidget ) { m_editPatch.contentLayout->insertWidget( 0, m_customWidget ); m_customWidget->show(); qCDebug(PLUGIN_PATCHREVIEW) << "got custom widget"; } bool showTests = false; QMap files = ipatch->additionalSelectableFiles(); QMap::const_iterator it = files.constBegin(); for (; it != files.constEnd(); ++it) { auto project = ICore::self()->projectController()->findProjectForUrl(it.key()); if (project && !ICore::self()->testController()->testSuitesForProject(project).isEmpty()) { showTests = true; break; } } m_editPatch.testsButton->setVisible(showTests); m_editPatch.testProgressBar->hide(); } void PatchReviewToolView::slotAppliedChanged( int newState ) { if ( LocalPatchSource* lpatch = GetLocalPatchSource() ) { lpatch->setAlreadyApplied( newState == Qt::Checked ); m_plugin->notifyPatchChanged(); } } void PatchReviewToolView::showEditDialog() { m_editPatch.setupUi( this ); bool allowSelection = m_plugin->patch() && m_plugin->patch()->canSelectFiles(); m_fileModel = new PatchFilesModel( this, allowSelection ); m_fileSortProxyModel = new VcsFileChangesSortProxyModel(this); m_fileSortProxyModel->setSourceModel(m_fileModel); m_fileSortProxyModel->sort(1); m_fileSortProxyModel->setDynamicSortFilter(true); m_editPatch.filesList->setModel( m_fileSortProxyModel ); m_editPatch.filesList->header()->hide(); m_editPatch.filesList->setRootIsDecorated( false ); m_editPatch.filesList->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_editPatch.filesList, &QTreeView::customContextMenuRequested, this, &PatchReviewToolView::customContextMenuRequested); connect(m_fileModel, &PatchFilesModel::itemChanged, this, &PatchReviewToolView::fileItemChanged); m_editPatch.finishReview->setDefaultAction(m_plugin->finishReviewAction()); #ifdef WITH_PURPOSE m_exportMenu = new Purpose::Menu(this); connect(m_exportMenu, &Purpose::Menu::finished, this, [](const QJsonObject &output, int error, const QString &errorMessage) { Sublime::Message* message; if (error==0) { const QString messageText = i18n("You can find the new request at:
%1 ", output[QLatin1String("url")].toString()); message = new Sublime::Message(messageText, Sublime::Message::Information); } else { const QString messageText = i18n("Couldn't export the patch.\n%1", errorMessage); message = new Sublime::Message(messageText, Sublime::Message::Error); } ICore::self()->uiController()->postMessage(message); }); // set the model input parameters to avoid terminal warnings m_exportMenu->model()->setInputData(QJsonObject { { QStringLiteral("urls"), QJsonArray { QString() } }, { QStringLiteral("mimeType"), { QStringLiteral("text/x-patch") } } }); m_exportMenu->model()->setPluginType(QStringLiteral("Export")); m_editPatch.exportReview->setMenu( m_exportMenu ); #else m_editPatch.exportReview->setEnabled(false); #endif connect( m_editPatch.previousHunk, &QToolButton::clicked, this, &PatchReviewToolView::prevHunk ); connect( m_editPatch.nextHunk, &QToolButton::clicked, this, &PatchReviewToolView::nextHunk ); connect( m_editPatch.previousFile, &QToolButton::clicked, this, &PatchReviewToolView::prevFile ); connect( m_editPatch.nextFile, &QToolButton::clicked, this, &PatchReviewToolView::nextFile ); connect( m_editPatch.filesList, &QTreeView::activated , this, &PatchReviewToolView::fileDoubleClicked ); connect( m_editPatch.cancelReview, &QPushButton::clicked, m_plugin, &PatchReviewPlugin::cancelReview ); //connect( m_editPatch.cancelButton, SIGNAL(pressed()), this, SLOT(slotEditCancel()) ); //connect( this, SIGNAL(finished(int)), this, SLOT(slotEditDialogFinished(int)) ); connect( m_editPatch.updateButton, &QPushButton::clicked, m_plugin, &PatchReviewPlugin::forceUpdate ); connect( m_editPatch.testsButton, &QPushButton::clicked, this, &PatchReviewToolView::runTests ); m_selectAllAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-select-all")), i18n("Select All"), this ); connect( m_selectAllAction, &QAction::triggered, this, &PatchReviewToolView::selectAll ); m_deselectAllAction = new QAction( i18n("Deselect All"), this ); connect( m_deselectAllAction, &QAction::triggered, this, &PatchReviewToolView::deselectAll ); } void PatchReviewToolView::customContextMenuRequested(const QPoint& pos) { QList urls; const QModelIndexList selectionIdxs = m_editPatch.filesList->selectionModel()->selectedIndexes(); urls.reserve(selectionIdxs.size()); for (const QModelIndex& idx : selectionIdxs) { urls += idx.data(KDevelop::VcsFileChangesModel::UrlRole).toUrl(); } QPointer menu = new QMenu(m_editPatch.filesList); QList extensions; if(!urls.isEmpty()) { KDevelop::FileContext context(urls); extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions(&context, menu); } QList vcsActions; for (const ContextMenuExtension& ext : qAsConst(extensions)) { vcsActions += ext.actions(ContextMenuExtension::VcsGroup); } menu->addAction(m_selectAllAction); menu->addAction(m_deselectAllAction); menu->addActions(vcsActions); menu->exec(m_editPatch.filesList->viewport()->mapToGlobal(pos)); delete menu; } void PatchReviewToolView::nextHunk() { IDocument* current = ICore::self()->documentController()->activeDocument(); if(current && current->textDocument()) m_plugin->seekHunk( true, current->textDocument()->url() ); } void PatchReviewToolView::prevHunk() { IDocument* current = ICore::self()->documentController()->activeDocument(); if(current && current->textDocument()) m_plugin->seekHunk( false, current->textDocument()->url() ); } void PatchReviewToolView::seekFile(bool forwards) { if(!m_plugin->patch()) return; - QList checkedUrls = m_fileModel->checkedUrls(); + const QList checkedUrls = m_fileModel->checkedUrls(); QList allUrls = m_fileModel->urls(); IDocument* current = ICore::self()->documentController()->activeDocument(); if(!current || checkedUrls.empty()) return; qCDebug(PLUGIN_PATCHREVIEW) << "seeking direction" << forwards; int currentIndex = allUrls.indexOf(current->url()); QUrl newUrl; if((forwards && current->url() == checkedUrls.back()) || (!forwards && current->url() == checkedUrls[0])) { newUrl = m_plugin->patch()->file(); qCDebug(PLUGIN_PATCHREVIEW) << "jumping to patch"; } else if(current->url() == m_plugin->patch()->file() || currentIndex == -1) { if(forwards) newUrl = checkedUrls[0]; else newUrl = checkedUrls.back(); qCDebug(PLUGIN_PATCHREVIEW) << "jumping from patch"; } else { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QSet checkedUrlsSet(checkedUrls.begin(), checkedUrls.end()); +#else QSet checkedUrlsSet( checkedUrls.toSet() ); +#endif for(int offset = 1; offset < allUrls.size(); ++offset) { int pos; if(forwards) { pos = (currentIndex + offset) % allUrls.size(); }else{ pos = currentIndex - offset; if(pos < 0) pos += allUrls.size(); } if(checkedUrlsSet.contains(allUrls[pos])) { newUrl = allUrls[pos]; break; } } } if(newUrl.isValid()) { open( newUrl, true ); }else{ qCDebug(PLUGIN_PATCHREVIEW) << "found no valid target url"; } } void PatchReviewToolView::open( const QUrl& url, bool activate ) const { qCDebug(PLUGIN_PATCHREVIEW) << "activating url" << url; // If the document is already open in this area, just re-activate it if(KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url)) { const auto views = ICore::self()->uiController()->activeArea()->views(); for (Sublime::View* view : views) { if(view->document() == dynamic_cast(doc)) { if (activate) { // use openDocument() for the activation so that the document is added to File/Open Recent. ICore::self()->documentController()->openDocument(doc->url(), KTextEditor::Range::invalid()); } return; } } } QStandardItem* item = m_fileModel->itemForUrl( url ); IDocument* buddyDoc = nullptr; if (m_plugin->patch() && item) { for (int preRow = item->row() - 1; preRow >= 0; --preRow) { QStandardItem* preItem = m_fileModel->item(preRow); if (!m_fileModel->isCheckable() || preItem->checkState() == Qt::Checked) { // found valid predecessor, take it as buddy buddyDoc = ICore::self()->documentController()->documentForUrl(preItem->index().data(VcsFileChangesModel::UrlRole).toUrl()); if (buddyDoc) { break; } } } if (!buddyDoc) { buddyDoc = ICore::self()->documentController()->documentForUrl(m_plugin->patch()->file()); } } // we simplify and assume that documents to be opened without activating them also need not be // added to the Files/Open Recent menu. IDocument* newDoc = ICore::self()->documentController()->openDocument(url, KTextEditor::Range::invalid(), activate ? IDocumentController::DefaultMode : IDocumentController::DoNotActivate|IDocumentController::DoNotAddToRecentOpen, QString(), buddyDoc); KTextEditor::View* view = nullptr; if(newDoc) view = newDoc->activeTextView(); if(view && view->cursorPosition().line() == 0) m_plugin->seekHunk( true, url ); } void PatchReviewToolView::fileItemChanged( QStandardItem* item ) { if (item->column() != 0 || !m_plugin->patch()) return; QUrl url = item->index().data(VcsFileChangesModel::UrlRole).toUrl(); if (url.isEmpty()) return; KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if(m_fileModel->isCheckable() && item->checkState() != Qt::Checked) { // The file was deselected, so eventually close it if(doc && doc->state() == IDocument::Clean) { const auto views = ICore::self()->uiController()->activeArea()->views(); for (Sublime::View* view : views) { if(view->document() == dynamic_cast(doc)) { ICore::self()->uiController()->activeArea()->closeView(view); return; } } } } else if (!doc) { // Maybe the file was unchecked before, or it was just loaded. open( url, false ); } } void PatchReviewToolView::nextFile() { seekFile(true); } void PatchReviewToolView::prevFile() { seekFile(false); } void PatchReviewToolView::deselectAll() { m_fileModel->setAllChecked(false); } void PatchReviewToolView::selectAll() { m_fileModel->setAllChecked(true); } void PatchReviewToolView::finishReview() { QList selectedUrls = m_fileModel->checkedUrls(); qCDebug(PLUGIN_PATCHREVIEW) << "finishing review with" << selectedUrls; m_plugin->finishReview( selectedUrls ); } void PatchReviewToolView::fileDoubleClicked( const QModelIndex& idx ) { const QUrl file = idx.data(VcsFileChangesModel::UrlRole).toUrl(); open( file, true ); } void PatchReviewToolView::kompareModelChanged() { QList oldCheckedUrls = m_fileModel->checkedUrls(); m_fileModel->clear(); if ( !m_plugin->modelList() ) return; QMap additionalUrls = m_plugin->patch()->additionalSelectableFiles(); const Diff2::DiffModelList* models = m_plugin->modelList()->models(); if( models ) { for (auto* model : *models) { const Diff2::DifferenceList* diffs = model->differences(); int cnt = 0; if ( diffs ) cnt = diffs->count(); const QUrl file = m_plugin->urlForFileModel(model); if( file.isLocalFile() && !QFileInfo( file.toLocalFile() ).isReadable() ) continue; VcsStatusInfo status; status.setUrl( file ); status.setState( cnt>0 ? VcsStatusInfo::ItemModified : VcsStatusInfo::ItemUpToDate ); m_fileModel->updateState( status, cnt ); } } for( QMap::const_iterator it = additionalUrls.constBegin(); it != additionalUrls.constEnd(); ++it ) { VcsStatusInfo status; status.setUrl( it.key() ); status.setState( it.value() ); m_fileModel->updateState( status ); } if(!m_resetCheckedUrls) m_fileModel->setCheckedUrls(oldCheckedUrls); else m_resetCheckedUrls = false; m_editPatch.filesList->resizeColumnToContents( 0 ); // Eventually select the active document documentActivated( ICore::self()->documentController()->activeDocument() ); } void PatchReviewToolView::documentActivated( IDocument* doc ) { if( !doc ) return; if ( !m_plugin->modelList() ) return; const auto matches = m_fileSortProxyModel->match( m_fileSortProxyModel->index(0, 0), VcsFileChangesModel::UrlRole, doc->url(), 1, Qt::MatchExactly); m_editPatch.filesList->setCurrentIndex(matches.value(0)); } void PatchReviewToolView::runTests() { IPatchSource::Ptr ipatch = m_plugin->patch(); if ( !ipatch ) { return; } IProject* project = nullptr; QMap files = ipatch->additionalSelectableFiles(); QMap::const_iterator it = files.constBegin(); for (; it != files.constEnd(); ++it) { project = ICore::self()->projectController()->findProjectForUrl(it.key()); if (project) { break; } } if (!project) { return; } m_editPatch.testProgressBar->setFormat(i18n("Running tests: %p%")); m_editPatch.testProgressBar->setValue(0); m_editPatch.testProgressBar->show(); auto* job = new ProjectTestJob(project, this); connect(job, &ProjectTestJob::finished, this, &PatchReviewToolView::testJobResult); connect(job, SIGNAL(percent(KJob*,ulong)), this, SLOT(testJobPercent(KJob*,ulong))); ICore::self()->runController()->registerJob(job); } void PatchReviewToolView::testJobPercent(KJob* job, ulong percent) { Q_UNUSED(job); m_editPatch.testProgressBar->setValue(percent); } void PatchReviewToolView::testJobResult(KJob* job) { auto* testJob = qobject_cast(job); if (!testJob) { return; } ProjectTestResult result = testJob->testResult(); QString format; if (result.passed > 0 && result.failed == 0 && result.error == 0) { format = i18np("Test passed", "All %1 tests passed", result.passed); } else { format = i18n("Test results: %1 passed, %2 failed, %3 errors", result.passed, result.failed, result.error); } m_editPatch.testProgressBar->setFormat(format); // Needed because some test jobs may raise their own output views ICore::self()->uiController()->raiseToolView(this); } #include "patchreviewtoolview.moc" diff --git a/plugins/perforce/kdevperforce.json b/plugins/perforce/kdevperforce.json index 8905798350..2b4c19d1a4 100644 --- a/plugins/perforce/kdevperforce.json +++ b/plugins/perforce/kdevperforce.json @@ -1,93 +1,89 @@ { "KPlugin": { "Authors": [ { "Name": "Morten Danielsen Volden", "Name[ca@valencia]": "Morten Danielsen Volden", "Name[ca]": "Morten Danielsen Volden", "Name[cs]": "Morten Danielsen Volden", "Name[de]": "Morten Danielsen Volden", - "Name[el]": "Morten Danielsen Volden", "Name[en_GB]": "Morten Danielsen Volden", "Name[es]": "Morten Danielsen Volden", "Name[et]": "Morten Danielsen Volden", "Name[fr]": "Morten Danielsen Volden", "Name[gl]": "Morten Danielsen Volden", "Name[it]": "Morten Danielsen Volden", "Name[nl]": "Morten Danielsen Volden", "Name[nn]": "Morten Danielsen Volden", "Name[pl]": "Morten Danielsen Volden", "Name[pt]": "Morten Danielsen Volden", "Name[pt_BR]": "Morten Danielsen Volden", "Name[ru]": "Morten Danielsen Volden", "Name[sk]": "Morten Danielsen Volden", "Name[sl]": "Morten Danielsen Volden", "Name[sv]": "Morten Danielsen Volden", "Name[tr]": "Morten Danielsen Volden", "Name[uk]": "Morten Danielsen Volden", "Name[x-test]": "xxMorten Danielsen Voldenxx", "Name[zh_CN]": "Morten Danielsen Volden" } ], "Category": "Version Control", "Description": "Provides Integration with Perforce Version Control System", "Description[ca@valencia]": "Proporciona integració amb el sistema de control de versions Perforce", "Description[ca]": "Proporciona integració amb el sistema de control de versions Perforce", "Description[cs]": "Poskytuje integraci Perforce Version Control System", "Description[de]": "Bietet Integration mit der Perforce-Versionsverwaltung", - "Description[el]": "Παρέχει ενσωμάτωση με το σύστημα ελέγχου εκδόσεων Perforce", "Description[en_GB]": "Provides Integration with Perforce Version Control System", "Description[es]": "Proporciona integración con el sistema de control de versiones Perforce", "Description[et]": "See võimaldab lõimimist Perforce'i versioonihaldussüsteemiga", "Description[fr]": "Fournit une intégration avec le système de contrôle de version Perforce", "Description[gl]": "Fornece integración co sistema de control de versións Perforce.", "Description[it]": "Fornisce l'integrazione con il sistema di controllo versione Perforce", "Description[nl]": "Biedt ondersteuning voor Perforce versiecontrolesysteem", "Description[pl]": "Wplata zarządzanie wersjami Perforce", - "Description[pt]": "Oferece a integração com o Sistema de Controlo de Versões Perforce", + "Description[pt]": "Oferece a Integração com o Sistema de Controlo de Versões Perforce", "Description[pt_BR]": "Oferece a integração com o Sistema de Controle de Versões Perforce", "Description[sk]": "Poskytuje integráciu s verzionovacím systémom Perforce", "Description[sl]": "Ponuja vgradnjo sistema za nadzor različic Perforce", "Description[sv]": "Tillhandahåller integrering med Perforce versionskontrollsystem", "Description[tr]": "Perforce Sürüm Denetim Sistemi ile Bütünleşme Sağlar", "Description[uk]": "Забезпечує інтеграцію з системою керування версіями Perforce", "Description[x-test]": "xxProvides Integration with Perforce Version Control Systemxx", "Description[zh_CN]": "提供与 Perforce 版本控制系统的整合", "Icon": "kdevelop", "Id": "kdevperforce", "License": "GPL", "Name": "Perforce Support", "Name[ca@valencia]": "Implementació del Perforce", "Name[ca]": "Implementació del Perforce", "Name[cs]": "Podpora Perforce", "Name[de]": "Perforce-Unterstützung", - "Name[el]": "Υποστήριξη perforce", "Name[en_GB]": "Perforce Support", "Name[es]": "Implementación de Perforce", - "Name[et]": "Perforce'i toetus", "Name[fr]": "Prise en charge de Perforce", "Name[gl]": "Compatibilidade con Perforce", "Name[it]": "Supporto per Perforce", "Name[nl]": "Ondersteuning van Perforce", "Name[pl]": "Obsługa Perforce", "Name[pt]": "Suporte para o Perforce", "Name[pt_BR]": "Suporte ao Perforce", "Name[sk]": "Podpora Perforce", "Name[sl]": "Podpora za Perforce", "Name[sv]": "Perforce-stöd", "Name[tr]": "Perforce Desteği", "Name[uk]": "Підтримка Perforce", "Name[x-test]": "xxPerforce Supportxx", "Name[zh_CN]": "Perforce 支持", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "5.0" }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl", "org.kdevelop.ICentralizedVersionControl" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/perforce/ui/perforceimportmetadatawidget.cpp b/plugins/perforce/ui/perforceimportmetadatawidget.cpp index 60bf919104..98c3fc4bea 100644 --- a/plugins/perforce/ui/perforceimportmetadatawidget.cpp +++ b/plugins/perforce/ui/perforceimportmetadatawidget.cpp @@ -1,214 +1,214 @@ /*************************************************************************** * This file is part of KDevelop Perforce plugin, KDE project * * * * Copyright 2018 Morten Danielsen Volden * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "perforceimportmetadatawidget.h" #include #include #include #include #include using namespace KDevelop; PerforceImportMetadataWidget::PerforceImportMetadataWidget(QWidget* parent) : VcsImportMetadataWidget(parent) , m_ui(new Ui::PerforceImportMetadataWidget) { m_ui->setupUi(this); m_ui->executableLoc->setText("/usr/bin/p4"); m_ui->p4portEdit->setText("perforce:1666"); QProcessEnvironment curEnv = QProcessEnvironment::systemEnvironment(); m_ui->p4configEdit->setText(curEnv.contains("P4CONFIG") ? curEnv.value("P4CONFIG") : ""); m_ui->p4portEdit->setText(curEnv.contains("P4PORT") ? curEnv.value("P4PORT") : ""); m_ui->p4userEdit->setText(curEnv.contains("P4USER") ? curEnv.value("P4USER") : ""); curEnv.contains("P4CONFIG") ? m_ui->radioButtonConfig->setChecked(true) : m_ui->radioButtonVariables->setChecked(true); curEnv.contains("P4CONFIG") ? m_ui->p4configEdit->setEnabled(true) : m_ui->p4configEdit->setEnabled(false); m_ui->sourceLoc->setEnabled(false); m_ui->sourceLoc->setMode(KFile::Directory); m_ui->errorMsg->setTextColor(QColor(255, 0, 0)); m_ui->errorMsg->setReadOnly(true); m_ui->p4clientEdit->setEditable(true); connect(m_ui->p4clientEdit, QOverload<>::of(&KComboBox::returnPressed), this, &PerforceImportMetadataWidget::changed); connect(m_ui->radioButtonConfig, &QRadioButton::clicked, m_ui->p4configEdit, &QLineEdit::setEnabled); connect(m_ui->radioButtonVariables, &QRadioButton::clicked, m_ui->p4configEdit, &QLineEdit::setDisabled); connect(m_ui->testP4setupButton, &QPushButton::pressed, this, &PerforceImportMetadataWidget::testP4setup); } QUrl PerforceImportMetadataWidget::source() const { return m_ui->sourceLoc->url(); } VcsLocation PerforceImportMetadataWidget::destination() const { VcsLocation dest; dest.setRepositoryServer(m_ui->p4portEdit->text()); dest.setUserData(QVariant::fromValue(m_ui->p4userEdit->text())); dest.setRepositoryBranch(m_ui->p4clientEdit->itemText(0)); return dest; } QString PerforceImportMetadataWidget::message() const { return QString(); //TODO: return m_ui->message->toPlainText(); } void PerforceImportMetadataWidget::setSourceLocation(const VcsLocation& url) { m_ui->sourceLoc->setUrl(url.localUrl()); } void PerforceImportMetadataWidget::setSourceLocationEditable(bool enable) { m_ui->sourceLoc->setEnabled(enable); } void PerforceImportMetadataWidget::setMessage(const QString& message) { Q_UNUSED(message); //FIXME: correct ui field needs to be set //m_ui->message->setText(message); } bool PerforceImportMetadataWidget::hasValidData() const { // FIXME: It has valid data if testP4setup has completed correctly. AND client name has been set to something return !m_ui->p4clientEdit->itemText(0).isEmpty(); } void PerforceImportMetadataWidget::testP4setup() { m_ui->errorMsg->clear(); m_ui->p4clientEdit->clear(); if (!validateP4executable()) return; QDir execDir(m_ui->sourceLoc->url().toLocalFile()); QTemporaryDir tmpDir; if (!execDir.exists()) execDir.setPath(tmpDir.path()); if(!validateP4port(execDir.path())) return; if(!validateP4user(execDir.path())) return; emit changed(); } bool PerforceImportMetadataWidget::validateP4executable() { if (QStandardPaths::findExecutable(m_ui->executableLoc->url().toLocalFile()).isEmpty()) { m_ui->errorMsg->setText("Unable to find perforce executable. Is it installed on the system? Is it in your PATH?"); return false; } return true; } bool PerforceImportMetadataWidget::validateP4user(const QString& projectDir) const { QProcess exec; QProcessEnvironment p4execEnvironment; p4execEnvironment.insert(QString("P4PORT"), m_ui->p4portEdit->displayText()); exec.setWorkingDirectory(projectDir); exec.setProcessEnvironment(p4execEnvironment); exec.start(m_ui->executableLoc->url().toLocalFile(), QStringList{QStringLiteral("workspaces"), QStringLiteral("-u"), m_ui->p4userEdit->text()} ); exec.waitForFinished(); QString processStdout(exec.readAllStandardOutput()); QString processStderr(exec.readAllStandardError()); // std::cout << "Exited with code: " << exec.exitCode() << std::endl; // std::cout << "Exited with stdout" << processStdout.toStdString() << std::endl; // std::cout << "Exited with stderr" << processStderr.toStdString() << std::endl; if (exec.exitCode() != 0) { if(!processStderr.isEmpty()) { m_ui->errorMsg->setText(processStderr); } else { QString msg("P4 Client failed with exit code: "); msg += QString::number(exec.exitCode()); m_ui->errorMsg->setText(msg); } return false; } if(!processStdout.isEmpty()) { - QStringList clientCmdOutput = processStdout.split(QLatin1Char('\n'),QString::SkipEmptyParts); + const QStringList clientCmdOutput = processStdout.split(QLatin1Char('\n'),QString::SkipEmptyParts); QStringList clientItems; clientItems.reserve(clientCmdOutput.size()); for(QString const& clientLine : clientCmdOutput) { QStringList wordsInLine = clientLine.split(QLatin1Char(' ')); // Client mvo_testkdevinteg 2017/05/22 root C:\P4repo 'Created by mvo. ' -- Line would be expected to look like so clientItems.append(wordsInLine.at(1)); } m_ui->p4clientEdit->addItems(clientItems); } return true; } bool PerforceImportMetadataWidget::validateP4port(const QString& projectDir) const { QProcess exec; QProcessEnvironment p4execEnvironment; p4execEnvironment.insert(QString("P4PORT"), m_ui->p4portEdit->displayText()); QTextStream out(stdout); const auto& env = p4execEnvironment.toStringList(); for (const QString& x : env) { out << x << QLatin1Char('\n'); } out.flush(); exec.setWorkingDirectory(projectDir); exec.setProcessEnvironment(p4execEnvironment); exec.start(m_ui->executableLoc->url().toLocalFile(), QStringList() << QStringLiteral("info")); exec.waitForFinished(); //QString processStdout(exec.readAllStandardOutput()); QString processStderr(exec.readAllStandardError()); //std::cout << "Exited with code: " << exec.exitCode() << std::endl; //std::cout << "Exited with stdout" << processStdout.toStdString() << std::endl; //std::cout << "Exited with stderr" << processStderr.toStdString() << std::endl; if (exec.exitCode() != 0) { if(!processStderr.isEmpty()) { m_ui->errorMsg->setText(processStderr); } else { QString msg("P4 Client failed with error code: "); msg += QString::number(exec.exitCode()); m_ui->errorMsg->setText(msg); } return false; } return true; } diff --git a/plugins/problemreporter/kdevproblemreporter.json b/plugins/problemreporter/kdevproblemreporter.json index 8dd2faec0e..0c15131f56 100644 --- a/plugins/problemreporter/kdevproblemreporter.json +++ b/plugins/problemreporter/kdevproblemreporter.json @@ -1,101 +1,87 @@ { "KPlugin": { "Authors": [ { "Name": "Hamish Rodda", "Name[ca@valencia]": "Hamish Rodda", "Name[ca]": "Hamish Rodda", "Name[cs]": "Hamish Rodda", "Name[de]": "Hamish Rodda", - "Name[el]": "Hamish Rodda", "Name[en_GB]": "Hamish Rodda", "Name[es]": "Hamish Rodda", "Name[et]": "Hamish Rodda", "Name[fr]": "Hamish Rodda", "Name[gl]": "Hamish Rodda", "Name[it]": "Hamish Rodda", "Name[nl]": "Hamish Rodda", "Name[nn]": "Hamish Rodda", "Name[pl]": "Hamish Rodda", "Name[pt]": "Hamish Rodda", "Name[pt_BR]": "Hamish Rodda", "Name[ru]": "Hamish Rodda", "Name[sk]": "Hamish Rodda", "Name[sl]": "Hamish Rodda", "Name[sv]": "Hamish Rodda", "Name[tr]": "Hamish Rodda", "Name[uk]": "Hamish Rodda", "Name[x-test]": "xxHamish Roddaxx", "Name[zh_CN]": "Hamish Rodda" } ], "Category": "Utilities", "Description": "This plugin shows errors in source code.", - "Description[ar]": "تظهر هذه الملحقة الأخطاء في الشِّفرة المصدريّة.", "Description[ca@valencia]": "Aquest connector permet mostrar errors en el codi font.", "Description[ca]": "Aquest connector permet mostrar errors en el codi font.", "Description[cs]": "Tento zásuvný modul ukazuje chyby ve zdrojovém kódu.", "Description[de]": "Dieses Modul zeigt Fehler im Quelltext an.", - "Description[el]": "Αυτό το πρόσθετο εμφανίζει τα λάθη στον πηγαίο κώδικα.", "Description[en_GB]": "This plugin shows errors in source code.", "Description[es]": "Este complemento muestra errores en el código fuente.", "Description[et]": "See plugin näitab vigu lähtekoodis.", "Description[fr]": "Ce module affiche les erreurs dans le code source.", "Description[gl]": "Este complemento mostra erros no código fonte.", "Description[it]": "Questa estensione mostra gli errori nel codice sorgente.", "Description[nl]": "Deze plugin toont fouten in broncode", "Description[pl]": "Pokazuje błędy w kodzie źródłowym.", "Description[pt]": "Este 'plugin' mostra os erros no código-fonte.", "Description[pt_BR]": "Este plugin apresenta os erros no código-fonte.", "Description[sk]": "Tento plugin ukáže chyby v zdrojovom kóde.", "Description[sl]": "Vstavek prikazuje napake v izvorni kodi.", "Description[sv]": "Insticksprogrammet visar fel i källkod.", "Description[tr]": "Bu eklenti kaynak koddaki hataları gösterir.", "Description[uk]": "За допомогою цього додатка можна переглянути повідомлення про помилки у коді.", "Description[x-test]": "xxThis plugin shows errors in source code.xx", "Description[zh_CN]": "此插件显示源代码中的错误。", - "Description[zh_TW]": "此外掛程式顯示源碼中的錯誤。", "Icon": "emblem-important", "Id": "kdevproblemreporter", "License": "LGPL", "Name": "Problem Reporter View", - "Name[ar]": "عرض مبلّغ المشاكل", - "Name[bg]": "Преглед на съобщенията за грешки", - "Name[bs]": "Pregled pomoću prikazivača problema", "Name[ca@valencia]": "Vista del notificador de problemes", "Name[ca]": "Vista del notificador de problemes", "Name[cs]": "Pohled oznamovatele problému", - "Name[da]": "Visning af problemrapportering", "Name[de]": "Ansicht für Fehlerberichte", - "Name[el]": "Προβολή ανταποκριτή προβλημάτων", "Name[en_GB]": "Problem Reporter View", "Name[es]": "Vista del notificador de problemas", "Name[et]": "Probleemide teavitaja vaade", "Name[fr]": "Vue du rapporteur de problèmes", "Name[gl]": "Vista do relator de problemas", - "Name[hu]": "Hibajelentő nézet", "Name[it]": "Vista segnalazione problemi", - "Name[kk]": "Мәселе хабарлағыш көрінісі", "Name[nb]": "Problemmeldervisning", - "Name[nds]": "Problemsöök-Ansicht", "Name[nl]": "Probleemrapporteuroverzicht", "Name[pl]": "Widok zgłaszania problemów", "Name[pt]": "Área de Relatórios de Erros", "Name[pt_BR]": "Área do relatórios de erros", "Name[ru]": "Панель диагностики проблем", "Name[sk]": "Pohľad na zadávač problémov", "Name[sl]": "Prikaz poročevalca o težavah", "Name[sv]": "Problemrapporteringsvisning", "Name[tr]": "Sorun Bildirici Görünümü", - "Name[ug]": "مەسىلە مەلۇم قىلغۇچ كۆرۈنۈشى", "Name[uk]": "Перегляд інструмента звітування про проблеми", "Name[x-test]": "xxProblem Reporter Viewxx", "Name[zh_CN]": "错误汇报视图", - "Name[zh_TW]": "問題回報器檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/projectfilter/kdevprojectfilter.json b/plugins/projectfilter/kdevprojectfilter.json index 2f5f6f282b..97d4f6fe77 100644 --- a/plugins/projectfilter/kdevprojectfilter.json +++ b/plugins/projectfilter/kdevprojectfilter.json @@ -1,99 +1,88 @@ { "KPlugin": { "Authors": [ { "Name": "Milian Wolff", "Name[ca@valencia]": "Milian Wolff", "Name[ca]": "Milian Wolff", "Name[cs]": "Milian Wolff", "Name[de]": "Milian Wolff", - "Name[el]": "Milian Wolff", "Name[en_GB]": "Milian Wolff", "Name[es]": "Milian Wolff", "Name[et]": "Milian Wolff", "Name[fr]": "Milian Wolff", "Name[gl]": "Milian Wolff", "Name[it]": "Milian Wolff", "Name[nl]": "Milian Wolff", "Name[nn]": "Milian Wolff", "Name[pl]": "Milian Wolff", "Name[pt]": "Milian Wolff", "Name[pt_BR]": "Milian Wolff", "Name[ru]": "Milian Wolff", "Name[sk]": "Milian Wolff", "Name[sl]": "Milian Wolff", "Name[sv]": "Milian Wolff", "Name[tr]": "Milian Wolff", "Name[uk]": "Milian Wolff", "Name[x-test]": "xxMilian Wolffxx", "Name[zh_CN]": "Milian Wolff" } ], "Category": "Project Management", "Description": "Configure which files and folders inside the project folder should be included or excluded.", - "Description[ar]": "اضبط أيّ الملفّات والمجلّدات داخل مجلّد المشروع يجب تضمينها أو استثنائها.", "Description[ca@valencia]": "Configura quins fitxers i carpetes dins de la carpeta del projecte s'han d'incloure o excloure.", "Description[ca]": "Configura quins fitxers i carpetes dins de la carpeta del projecte s'han d'incloure o excloure.", "Description[de]": "Legt fest, welche Dateien und Ordner innerhalb des Projektordners ein- oder ausgeschlossen werden sollen.", - "Description[el]": "Διαμορφώνει τα αρχεία και τους φακέλους μέσα στο έργο που θα πρέπει να συμπεριλαμβάνονται ή να αποκλείονται.", "Description[en_GB]": "Configure which files and folders inside the project folder should be included or excluded.", "Description[es]": "Configurar los archivos y carpetas que pertenecen a la carpeta del proyecto y que deben ser incluidos o excluidos.", - "Description[et]": "Seadistamine, millised projektikataloogi failid ja kataloogid kaasa arvata või välja jätta.", + "Description[et]": "Määramine, millised projektikataloogi failid ja kataloogid kaasata või välja jätta.", "Description[fr]": "Configurer quels fichiers et dossiers à l'intérieur du dossier projet doivent être inclus ou exclus.", "Description[gl]": "Configurar cales ficheiros e cartafoles dentro do cartafol do proxecto deben incluírse ou excluírse.", "Description[it]": "Configura quali file e cartelle nella cartella del progetto devono essere incluse o escluse.", "Description[nl]": "Stel in welke bestanden en mappen in de projectmap meegenomen of uitgesloten moeten worden.", "Description[pl]": "Określa uwzględniane i wykluczane pliki i katalogi w projekcie.", "Description[pt]": "Configurar os ficheiros ou pastas, dentro da pasta do projecto, que deverão ser incluídos ou excluídos.", "Description[pt_BR]": "Configura os arquivos e pastas, dentro da pasta do projeto, que devem ser incluídos ou excluídos.", "Description[sk]": "Nastaviť, ktoré súbory a priečinky v priečinku projektu majú byť zahrnuté alebo vylúčené.", "Description[sl]": "Nastavi katere datoteke in mape znotraj mape projekta naj bodo vključene ali izključene.", "Description[sv]": "Anpassa vilka filer och kataloger inne i projektkatalogen som ska inkluderas eller exkluderas.", "Description[tr]": "Proje klasörü içerisindeki hangi dosya ve klasörlerin dahil edilip edilmeyeceğini yapılandır.", "Description[uk]": "За допомогою цього модуля можна визначити, які файли і теки у теці проєкту має бути включено або виключено з його складу.", "Description[x-test]": "xxConfigure which files and folders inside the project folder should be included or excluded.xx", "Description[zh_CN]": "配置工程文件中哪些文件被加入或排除。", - "Description[zh_TW]": "設定專案資料夾中要包含或排除哪些檔案與資料夾。", "Icon": "view-filter", "Id": "KDevProjectFilter", "Name": "Project Filter", - "Name[ar]": "مرشّح المشروع", "Name[ca@valencia]": "Filtre de projecte", "Name[ca]": "Filtre de projecte", "Name[cs]": "Filtr projektů", - "Name[da]": "Projektfilter", "Name[de]": "Projektfilter", - "Name[el]": "Φίλτρο έργου", "Name[en_GB]": "Project Filter", "Name[es]": "Filtro de proyectos", - "Name[et]": "Projektifilter", "Name[fr]": "Filtre de projet", "Name[gl]": "Filtro do proxecto", - "Name[hu]": "Projektszűrő", "Name[it]": "Filtro progetto", - "Name[kk]": "Жоба сүзгісі", "Name[nb]": "Prosjektfilter", "Name[nl]": "Projectfilter", "Name[pl]": "Filtr projektu", "Name[pt]": "Filtro de Projectos", "Name[pt_BR]": "Filtro de projetos", "Name[ru]": "Фильтр проекта", "Name[sk]": "Filter projektu", "Name[sl]": "Filter projektov", "Name[sv]": "Projektfilter", "Name[tr]": "Proje Süzgeci", "Name[uk]": "Фільтр проєкту", "Name[x-test]": "xxProject Filterxx", "Name[zh_CN]": "工程过滤器", - "Name[zh_TW]": "專案過濾器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectFilter" ], "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/projectmanagerview/cutcopypastehelpers.cpp b/plugins/projectmanagerview/cutcopypastehelpers.cpp index 87d8b83852..2353a9cd20 100644 --- a/plugins/projectmanagerview/cutcopypastehelpers.cpp +++ b/plugins/projectmanagerview/cutcopypastehelpers.cpp @@ -1,355 +1,355 @@ /* This file is part of KDevelop Copyright (C) 2017 Alexander Potashev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "cutcopypastehelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace CutCopyPasteHelpers { TaskInfo::TaskInfo(const TaskStatus status, const TaskType type, const Path::List& src, const Path& dest) : m_status(status), m_type(type), m_src(src), m_dest(dest) { } TaskInfo TaskInfo::createMove(const bool ok, const Path::List& src, const Path& dest) { return TaskInfo(ok ? TaskStatus::SUCCESS : TaskStatus::FAILURE, TaskType::MOVE, src, dest); } TaskInfo TaskInfo::createCopy(const bool ok, const Path::List& src, const Path& dest) { return TaskInfo(ok ? TaskStatus::SUCCESS : TaskStatus::FAILURE, TaskType::COPY, src, dest); } TaskInfo TaskInfo::createDeletion(const bool ok, const Path::List& src, const Path& dest) { return TaskInfo(ok ? TaskStatus::SUCCESS : TaskStatus::FAILURE, TaskType::DELETION, src, dest); } static QWidget* createPasteStatsWidget(QWidget *parent, const QVector& tasks) { // TODO: Create a model for the task list, and use it here instead of using QTreeWidget auto* treeWidget = new QTreeWidget(parent); QList items; items.reserve(tasks.size()); for (const TaskInfo& task : tasks) { int srcCount = task.m_src.size(); const bool withChildren = srcCount != 1; const QString destPath = task.m_dest.pathOrUrl(); QString text; if (withChildren) { // Multiple source items in the current suboperation switch (task.m_type) { case TaskType::MOVE: text = i18np("Move %1 item into %2", "Move %1 items into %2", srcCount, destPath); break; case TaskType::COPY: text = i18np("Copy %1 item into %2", "Copy %1 items into %2", srcCount, destPath); break; case TaskType::DELETION: text = i18np("Delete %1 item", "Delete %1 items", srcCount); break; } } else { // One source item in the current suboperation const QString srcPath = task.m_src[0].pathOrUrl(); switch (task.m_type) { case TaskType::MOVE: text = i18n("Move item %1 into %2", srcPath, destPath); break; case TaskType::COPY: text = i18n("Copy item %1 into %2", srcPath, destPath); break; case TaskType::DELETION: text = i18n("Delete item %1", srcPath); break; } } QString tooltip; QString iconName; switch (task.m_status) { case TaskStatus::SUCCESS: tooltip = i18n("Suboperation succeeded"); iconName = QStringLiteral("dialog-ok"); break; case TaskStatus::FAILURE: tooltip = i18n("Suboperation failed"); iconName = QStringLiteral("dialog-error"); break; case TaskStatus::SKIPPED: tooltip = i18n("Suboperation skipped to prevent data loss"); iconName = QStringLiteral("dialog-warning"); break; } auto* item = new QTreeWidgetItem; item->setText(0, text); item->setIcon(0, QIcon::fromTheme(iconName)); item->setToolTip(0, tooltip); items.append(item); if (withChildren) { for (const Path& src : task.m_src) { auto* childItem = new QTreeWidgetItem; childItem->setText(0, src.pathOrUrl()); item->addChild(childItem); } } } treeWidget->insertTopLevelItems(0, items); treeWidget->headerItem()->setHidden(true); return treeWidget; } SourceToDestinationMap mapSourceToDestination(const Path::List& sourcePaths, const Path& destinationPath) { // For example you are moving the following items into /dest/ // * /tests/ // * /tests/abc.cpp // If you pass them as is, moveFilesAndFolders() will crash (see note: // "Do not attempt to move subitems along with their parents"). // Thus we filter out subitems from "Path::List filteredPaths". // // /tests/abc.cpp will be implicitly moved to /dest/tests/abc.cpp, for // that reason we add "/dest/tests/abc.cpp" into "result.finalPaths" as well as // "/dest/tests". // // "result.finalPaths" will be used to highlight destination items after // copy/move. Path::List sortedPaths = sourcePaths; std::sort(sortedPaths.begin(), sortedPaths.end()); SourceToDestinationMap result; for (const Path& path : sortedPaths) { if (!result.filteredPaths.isEmpty() && result.filteredPaths.back().isParentOf(path)) { // think: "/tests" const Path& previousPath = result.filteredPaths.back(); // think: "/dest" + "/".relativePath("/tests/abc.cpp") = /dest/tests/abc.cpp result.finalPaths[previousPath].append(Path(destinationPath, previousPath.parent().relativePath(path))); } else { // think: "/tests" result.filteredPaths.append(path); // think: "/dest" + "tests" = "/dest/tests" result.finalPaths[path].append(Path(destinationPath, path.lastPathSegment())); } } return result; } struct ClassifiedPaths { // Items originating from projects open in this KDevelop session QHash> itemsPerProject; // Items that do not belong to known projects Path::List alienSrcPaths; }; static ClassifiedPaths classifyPaths(const Path::List& paths, KDevelop::ProjectModel* projectModel) { ClassifiedPaths result; for (const Path& path : paths) { - QList items = projectModel->itemsForPath(IndexedString(path.path())); + const QList items = projectModel->itemsForPath(IndexedString(path.path())); if (!items.empty()) { for (ProjectBaseItem* item : items) { IProject* project = item->project(); auto itemsIt = result.itemsPerProject.find(project); if (itemsIt == result.itemsPerProject.end()) { itemsIt = result.itemsPerProject.insert(project, QList()); } itemsIt->append(item); } } else { result.alienSrcPaths.append(path); } } return result; } QVector copyMoveItems(const Path::List& paths, ProjectBaseItem* destItem, const Operation operation) { KDevelop::ProjectModel* projectModel = KDevelop::ICore::self()->projectController()->projectModel(); const ClassifiedPaths cl = classifyPaths(paths, projectModel); QVector tasks; IProject* destProject = destItem->project(); IProjectFileManager* destProjectFileManager = destProject->projectFileManager(); ProjectFolderItem* destFolder = destItem->folder(); Path destPath = destFolder->path(); const auto& srcProjects = cl.itemsPerProject.keys(); for (IProject* srcProject : srcProjects) { const auto& itemsList = cl.itemsPerProject[srcProject]; Path::List pathsList; pathsList.reserve(itemsList.size()); for (KDevelop::ProjectBaseItem* item : itemsList) { pathsList.append(item->path()); } if (srcProject == destProject) { if (operation == Operation::CUT) { // Move inside project const bool ok = destProjectFileManager->moveFilesAndFolders(itemsList, destFolder); tasks.append(TaskInfo::createMove(ok, pathsList, destPath)); } else { // Copy inside project const bool ok = destProjectFileManager->copyFilesAndFolders(pathsList, destFolder); tasks.append(TaskInfo::createCopy(ok, pathsList, destPath)); } } else { // Copy/move between projects: // 1. Copy and add into destination project; // 2. Remove from source project. const bool copy_ok = destProjectFileManager->copyFilesAndFolders(pathsList, destFolder); tasks.append(TaskInfo::createCopy(copy_ok, pathsList, destPath)); if (operation == Operation::CUT) { if (copy_ok) { IProjectFileManager* srcProjectFileManager = srcProject->projectFileManager(); const bool deletion_ok = srcProjectFileManager->removeFilesAndFolders(itemsList); tasks.append(TaskInfo::createDeletion(deletion_ok, pathsList, destPath)); } else { tasks.append(TaskInfo(TaskStatus::SKIPPED, TaskType::DELETION, pathsList, destPath)); } } } } // Copy/move items from outside of all open projects if (!cl.alienSrcPaths.isEmpty()) { const bool alien_copy_ok = destProjectFileManager->copyFilesAndFolders(cl.alienSrcPaths, destFolder); tasks.append(TaskInfo::createCopy(alien_copy_ok, cl.alienSrcPaths, destPath)); if (operation == Operation::CUT) { if (alien_copy_ok) { QList urlsToDelete; urlsToDelete.reserve(cl.alienSrcPaths.size()); for (const Path& path : cl.alienSrcPaths) { urlsToDelete.append(path.toUrl()); } KIO::DeleteJob* deleteJob = KIO::del(urlsToDelete); const bool deletion_ok = deleteJob->exec(); tasks.append(TaskInfo::createDeletion(deletion_ok, cl.alienSrcPaths, destPath)); } else { tasks.append(TaskInfo(TaskStatus::SKIPPED, TaskType::DELETION, cl.alienSrcPaths, destPath)); } } } return tasks; } void showWarningDialogForFailedPaste(QWidget* parent, const QVector& tasks) { QDialog* dialog = new QDialog(parent); dialog->setWindowTitle(i18nc("@title:window", "Paste Failed")); auto *buttonBox = new QDialogButtonBox(dialog); buttonBox->setStandardButtons(QDialogButtonBox::Ok); QObject::connect(buttonBox, &QDialogButtonBox::clicked, dialog, &QDialog::accept); dialog->setWindowModality(Qt::WindowModal); dialog->setModal(true); QWidget* mainWidget = new QWidget(dialog); auto* mainLayout = new QVBoxLayout(mainWidget); const int spacingHint = mainWidget->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); mainLayout->setSpacing(spacingHint * 2); // provide extra spacing mainLayout->setMargin(0); auto* hLayout = new QHBoxLayout; hLayout->setMargin(0); hLayout->setSpacing(-1); // use default spacing mainLayout->addLayout(hLayout, 0); QLabel* iconLabel = new QLabel(mainWidget); // Icon QStyleOption option; option.initFrom(mainWidget); QIcon icon = QIcon::fromTheme(QStringLiteral("dialog-warning")); iconLabel->setPixmap(icon.pixmap(mainWidget->style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, mainWidget))); auto* iconLayout = new QVBoxLayout(); iconLayout->addStretch(1); iconLayout->addWidget(iconLabel); iconLayout->addStretch(5); hLayout->addLayout(iconLayout, 0); hLayout->addSpacing(spacingHint); const QString text = i18n("Failed to paste. Below is a list of suboperations that have been attempted."); QLabel* messageLabel = new QLabel(text, mainWidget); messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); hLayout->addWidget(messageLabel, 5); QWidget* statsWidget = createPasteStatsWidget(dialog, tasks); auto* topLayout = new QVBoxLayout; dialog->setLayout(topLayout); topLayout->addWidget(mainWidget); topLayout->addWidget(statsWidget, 1); topLayout->addWidget(buttonBox); dialog->setMinimumSize(300, qMax(150, qMax(iconLabel->sizeHint().height(), messageLabel->sizeHint().height()))); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } } // namespace CutCopyPasteHelpers diff --git a/plugins/projectmanagerview/kdevprojectmanagerview.json b/plugins/projectmanagerview/kdevprojectmanagerview.json index 31d44a0ee1..ddbb3241ae 100644 --- a/plugins/projectmanagerview/kdevprojectmanagerview.json +++ b/plugins/projectmanagerview/kdevprojectmanagerview.json @@ -1,101 +1,86 @@ { "KPlugin": { "Authors": [ { "Name": "Roberto Raggi", "Name[ca@valencia]": "Roberto Raggi", "Name[ca]": "Roberto Raggi", "Name[cs]": "Roberto Raggi", "Name[de]": "Roberto Raggi", - "Name[el]": "Roberto Raggi", "Name[en_GB]": "Roberto Raggi", "Name[es]": "Roberto Raggi", "Name[et]": "Roberto Raggi", "Name[fr]": "Roberto Raggi", "Name[gl]": "Roberto Raggi", "Name[it]": "Roberto Raggi", "Name[nl]": "Roberto Raggi", "Name[nn]": "Roberto Raggi", "Name[pl]": "Roberto Raggi", "Name[pt]": "Roberto Raggi", "Name[pt_BR]": "Roberto Raggi", "Name[ru]": "Roberto Raggi", "Name[sk]": "Roberto Raggi", "Name[sl]": "Roberto Raggi", "Name[sv]": "Roberto Raggi", "Name[tr]": "Roberto Raggi", "Name[uk]": "Roberto Raggi", "Name[x-test]": "xxRoberto Raggixx", "Name[zh_CN]": "Roberto Raggi" } ], "Category": "Core", "Description": "Lets you manage the project contents.", - "Description[ar]": "يتيح لك إدارة محتويات المشروع.", "Description[ca@valencia]": "Vos permet gestionar els continguts del projecte.", "Description[ca]": "Us permet gestionar els continguts del projecte.", "Description[cs]": "Umožňuje spravovat obsah projektu.", "Description[de]": "Lässt Sie den Inhalt Ihres Projekts verwalten.", - "Description[el]": "Σας επιτρέπει τη διαχείριση του περιεχομένου του έργου.", "Description[en_GB]": "Lets you manage the project contents.", "Description[es]": "Le permite gestionar el contenido del proyecto.", - "Description[et]": "Võimaldab hallata projektide sisu.", + "Description[et]": "Projektide sisu haldamine.", "Description[fr]": "Vous laisse gérer le contenu du projet.", "Description[gl]": "Permítelle xestionar os contidos do proxecto.", "Description[it]": "Consente di gestire i contenuti del progetto.", "Description[nl]": "Laat u de projectinhoud beheren.", "Description[pl]": "Umożliwia zarządzanie zawartością projektu.", "Description[pt]": "Permite-lhe gerir o conteúdo do projecto.", "Description[pt_BR]": "Permite-lhe gerenciar o conteúdo do projeto.", "Description[sk]": "Umožní vám spravovať obsah projektu.", "Description[sl]": "Pomaga vam upravljati z vsebino projekta.", "Description[sv]": "Låter dig hantera projektets innehåll.", "Description[tr]": "Proje içeriğini yönetmenizi sağlar.", "Description[uk]": "Надає вам змогу керувати вмістом проєктів.", "Description[x-test]": "xxLets you manage the project contents.xx", "Description[zh_CN]": "让您管理工程内容。", - "Description[zh_TW]": "讓您管理您的專案內容。", "Icon": "kdevelop", "Id": "KDevProjectManagerView", "License": "LGPL", "Name": "Project Manager View", - "Name[ar]": "عرض مدير المشاريع", - "Name[bg]": "Преглед на редактора на проекти", "Name[ca@valencia]": "Vista del gestor de projectes", "Name[ca]": "Vista del gestor de projectes", "Name[cs]": "Pohled správce projektů", - "Name[da]": "Visning af projekthåndtering", "Name[de]": "Ansicht für Projektverwaltung", - "Name[el]": "Προβολή διαχειριστή έργου", "Name[en_GB]": "Project Manager View", "Name[es]": "Vista del gestor de proyectos", - "Name[et]": "Projektihalduri vaade", "Name[fr]": "Vue du gestionnaire de projets", "Name[gl]": "Vista do xestor de proxectos", - "Name[hu]": "Projektkezelő nézet", "Name[it]": "Vista gestore progetto", - "Name[kk]": "Жоба менеджер көрінісі", "Name[nb]": "Prosjektbehandlervisning", - "Name[nds]": "Projektpleger-Ansicht", "Name[nl]": "Projectbeheerder-overzicht", - "Name[pa]": "ਪਰੋਜੈਕਟ ਮੈਨੇਜਰ ਝਲਕ", "Name[pl]": "Widok zarządzania projektem", "Name[pt]": "Área do Gestor de Projectos", "Name[pt_BR]": "Visualizador do gerenciador de projeto", "Name[ru]": "Панель управления проектами", "Name[sk]": "Pohľad na správcu projektu", "Name[sl]": "Pogled upravljalnika projektov", "Name[sv]": "Projekthanteringsvisning", "Name[tr]": "Proje Yöneticisi Görünümü", - "Name[ug]": "قۇرۇلۇش باشقۇرغۇ كۆرۈنۈشى", "Name[uk]": "Перегляд керування проєктами", "Name[x-test]": "xxProject Manager Viewxx", "Name[zh_CN]": "工程管理器视图", - "Name[zh_TW]": "專案管理員檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/projectmanagerview/projectmanagerviewplugin.cpp b/plugins/projectmanagerview/projectmanagerviewplugin.cpp index 161ff11d14..9442d6e3ac 100644 --- a/plugins/projectmanagerview/projectmanagerviewplugin.cpp +++ b/plugins/projectmanagerview/projectmanagerviewplugin.cpp @@ -1,801 +1,801 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2016, 2017 Alexander Potashev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectmanagerviewplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "projectmanagerview.h" #include "debug.h" #include "cutcopypastehelpers.h" using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(ProjectManagerFactory, "kdevprojectmanagerview.json", registerPlugin();) namespace { QAction* createSeparatorAction() { auto* separator = new QAction(nullptr); separator->setSeparator(true); return separator; } // Returns nullptr iff the list of URLs to copy/cut was empty QMimeData* createClipboardMimeData(const bool cut) { auto* ctx = dynamic_cast( ICore::self()->selectionController()->currentSelection()); QList urls; QList mostLocalUrls; const auto& items = ctx->items(); for (const ProjectBaseItem* item : items) { if (item->folder() || item->file()) { const QUrl& url = item->path().toUrl(); urls << url; mostLocalUrls << KFileItem(url).mostLocalUrl(); } } qCDebug(PLUGIN_PROJECTMANAGERVIEW) << urls; if (urls.isEmpty()) { return nullptr; } auto* mimeData = new QMimeData; KIO::setClipboardDataCut(mimeData, cut); KUrlMimeData::setUrls(urls, mostLocalUrls, mimeData); return mimeData; } } // anonymous namespace class KDevProjectManagerViewFactory: public KDevelop::IToolViewFactory { public: explicit KDevProjectManagerViewFactory( ProjectManagerViewPlugin *plugin ): mplugin( plugin ) {} QWidget* create( QWidget *parent = nullptr ) override { return new ProjectManagerView( mplugin, parent ); } Qt::DockWidgetArea defaultPosition() const override { return Qt::LeftDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.ProjectsView"); } private: ProjectManagerViewPlugin *mplugin; }; class ProjectManagerViewPluginPrivate { public: ProjectManagerViewPluginPrivate() {} KDevProjectManagerViewFactory *factory; QList ctxProjectItemList; QAction* m_buildAll; QAction* m_build; QAction* m_install; QAction* m_clean; QAction* m_configure; QAction* m_prune; }; static QList itemsFromIndexes(const QList& indexes) { QList items; ProjectModel* model = ICore::self()->projectController()->projectModel(); items.reserve(indexes.size()); for (const QModelIndex& index : indexes) { items += model->itemFromIndex(index); } return items; } ProjectManagerViewPlugin::ProjectManagerViewPlugin( QObject *parent, const QVariantList& ) : IPlugin( QStringLiteral("kdevprojectmanagerview"), parent ), d(new ProjectManagerViewPluginPrivate) { d->m_buildAll = new QAction( i18n("Build all Projects"), this ); d->m_buildAll->setIcon(QIcon::fromTheme(QStringLiteral("run-build"))); connect( d->m_buildAll, &QAction::triggered, this, &ProjectManagerViewPlugin::buildAllProjects ); actionCollection()->addAction( QStringLiteral("project_buildall"), d->m_buildAll ); d->m_build = new QAction( i18n("Build Selection"), this ); d->m_build->setIconText( i18n("Build") ); actionCollection()->setDefaultShortcut( d->m_build, Qt::Key_F8 ); d->m_build->setIcon(QIcon::fromTheme(QStringLiteral("run-build"))); d->m_build->setEnabled( false ); connect( d->m_build, &QAction::triggered, this, &ProjectManagerViewPlugin::buildProjectItems ); actionCollection()->addAction( QStringLiteral("project_build"), d->m_build ); d->m_install = new QAction( i18n("Install Selection"), this ); d->m_install->setIconText( i18n("Install") ); d->m_install->setIcon(QIcon::fromTheme(QStringLiteral("run-build-install"))); actionCollection()->setDefaultShortcut( d->m_install, Qt::SHIFT + Qt::Key_F8 ); d->m_install->setEnabled( false ); connect( d->m_install, &QAction::triggered, this, &ProjectManagerViewPlugin::installProjectItems ); actionCollection()->addAction( QStringLiteral("project_install"), d->m_install ); d->m_clean = new QAction( i18n("Clean Selection"), this ); d->m_clean->setIconText( i18n("Clean") ); d->m_clean->setIcon(QIcon::fromTheme(QStringLiteral("run-build-clean"))); d->m_clean->setEnabled( false ); connect( d->m_clean, &QAction::triggered, this, &ProjectManagerViewPlugin::cleanProjectItems ); actionCollection()->addAction( QStringLiteral("project_clean"), d->m_clean ); d->m_configure = new QAction( i18n("Configure Selection"), this ); d->m_configure->setMenuRole( QAction::NoRole ); // OSX: Be explicit about role, prevent hiding due to conflict with "Preferences..." menu item d->m_configure->setIconText( i18n("Configure") ); d->m_configure->setIcon(QIcon::fromTheme(QStringLiteral("run-build-configure"))); d->m_configure->setEnabled( false ); connect( d->m_configure, &QAction::triggered, this, &ProjectManagerViewPlugin::configureProjectItems ); actionCollection()->addAction( QStringLiteral("project_configure"), d->m_configure ); d->m_prune = new QAction( i18n("Prune Selection"), this ); d->m_prune->setIconText( i18n("Prune") ); d->m_prune->setIcon(QIcon::fromTheme(QStringLiteral("run-build-prune"))); d->m_prune->setEnabled( false ); connect( d->m_prune, &QAction::triggered, this, &ProjectManagerViewPlugin::pruneProjectItems ); actionCollection()->addAction( QStringLiteral("project_prune"), d->m_prune ); // only add the action so that its known in the actionCollection // and so that it's shortcut etc. pp. is restored // apparently that is not possible to be done in the view itself *sigh* actionCollection()->addAction( QStringLiteral("locate_document") ); setXMLFile( QStringLiteral("kdevprojectmanagerview.rc") ); d->factory = new KDevProjectManagerViewFactory( this ); core()->uiController()->addToolView( i18n("Projects"), d->factory ); connect(core()->selectionController(), &ISelectionController::selectionChanged, this, &ProjectManagerViewPlugin::updateActionState); connect(ICore::self()->projectController()->buildSetModel(), &KDevelop::ProjectBuildSetModel::rowsInserted, this, &ProjectManagerViewPlugin::updateFromBuildSetChange); connect(ICore::self()->projectController()->buildSetModel(), &KDevelop::ProjectBuildSetModel::rowsRemoved, this, &ProjectManagerViewPlugin::updateFromBuildSetChange); connect(ICore::self()->projectController()->buildSetModel(), &KDevelop::ProjectBuildSetModel::modelReset, this, &ProjectManagerViewPlugin::updateFromBuildSetChange); } void ProjectManagerViewPlugin::updateFromBuildSetChange() { updateActionState( core()->selectionController()->currentSelection() ); } void ProjectManagerViewPlugin::updateActionState( KDevelop::Context* ctx ) { bool isEmpty = ICore::self()->projectController()->buildSetModel()->items().isEmpty(); if( isEmpty ) { isEmpty = !ctx || ctx->type() != Context::ProjectItemContext || static_cast(ctx)->items().isEmpty(); } d->m_build->setEnabled( !isEmpty ); d->m_install->setEnabled( !isEmpty ); d->m_clean->setEnabled( !isEmpty ); d->m_configure->setEnabled( !isEmpty ); d->m_prune->setEnabled( !isEmpty ); } ProjectManagerViewPlugin::~ProjectManagerViewPlugin() { delete d; } void ProjectManagerViewPlugin::unload() { qCDebug(PLUGIN_PROJECTMANAGERVIEW) << "unloading manager view"; core()->uiController()->removeToolView(d->factory); } ContextMenuExtension ProjectManagerViewPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { if( context->type() != KDevelop::Context::ProjectItemContext ) return IPlugin::contextMenuExtension(context, parent); auto* ctx = static_cast(context); const QList items = ctx->items(); d->ctxProjectItemList.clear(); if( items.isEmpty() ) return IPlugin::contextMenuExtension(context, parent); //TODO: also needs: removeTarget, removeFileFromTarget, runTargetsFromContextMenu ContextMenuExtension menuExt; bool needsCreateFile = true; bool needsCreateFolder = true; bool needsCloseProjects = true; bool needsBuildItems = true; bool needsFolderItems = true; bool needsCutRenameRemove = true; bool needsRemoveTargetFiles = true; bool needsPaste = true; //needsCreateFile if there is one item and it's a folder or target needsCreateFile &= (items.count() == 1) && (items.first()->folder() || items.first()->target()); //needsCreateFolder if there is one item and it's a folder needsCreateFolder &= (items.count() == 1) && (items.first()->folder()); needsPaste = needsCreateFolder; d->ctxProjectItemList.reserve(items.size()); for (ProjectBaseItem* item : items) { d->ctxProjectItemList << item->index(); //needsBuildItems if items are limited to targets and buildfolders needsBuildItems &= item->target() || item->type() == ProjectBaseItem::BuildFolder; //needsCloseProjects if items are limited to top level folders (Project Folders) needsCloseProjects &= item->folder() && !item->folder()->parent(); //needsFolderItems if items are limited to folders needsFolderItems &= (bool)item->folder(); //needsRemove if items are limited to non-top-level folders or files that don't belong to targets needsCutRenameRemove &= (item->folder() && item->parent()) || (item->file() && !item->parent()->target()); //needsRemoveTargets if items are limited to file items with target parents needsRemoveTargetFiles &= (item->file() && item->parent()->target()); } if ( needsCreateFile ) { QAction* action = new QAction(i18n("Create &File..."), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::createFileFromContextMenu ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( needsCreateFolder ) { QAction* action = new QAction(i18n("Create F&older..."), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::createFolderFromContextMenu ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( needsBuildItems ) { QAction* action = new QAction(i18nc("@action", "&Build"), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("run-build"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::buildItemsFromContextMenu ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new QAction(i18nc("@action", "&Install"), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("run-build-install"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::installItemsFromContextMenu ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new QAction(i18nc("@action", "&Clean"), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("run-build-clean"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::cleanItemsFromContextMenu ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new QAction(i18n("&Add to Build Set"), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::addItemsFromContextMenuToBuildset ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); } if ( needsCloseProjects ) { QAction* close = new QAction(i18np("C&lose Project", "Close Projects", items.count()), parent); close->setIcon(QIcon::fromTheme(QStringLiteral("project-development-close"))); connect( close, &QAction::triggered, this, &ProjectManagerViewPlugin::closeProjects ); menuExt.addAction( ContextMenuExtension::ProjectGroup, close ); } if ( needsFolderItems ) { QAction* action = new QAction(i18n("&Reload"), parent); action->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); connect( action, &QAction::triggered, this, &ProjectManagerViewPlugin::reloadFromContextMenu ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } // Populating cut/copy/paste group if ( !menuExt.actions(ContextMenuExtension::FileGroup).isEmpty() ) { menuExt.addAction( ContextMenuExtension::FileGroup, createSeparatorAction() ); } if ( needsCutRenameRemove ) { QAction* cut = KStandardAction::cut(this, SLOT(cutFromContextMenu()), this); cut->setShortcutContext(Qt::WidgetShortcut); menuExt.addAction(ContextMenuExtension::FileGroup, cut); } { QAction* copy = KStandardAction::copy(this, SLOT(copyFromContextMenu()), this); copy->setShortcutContext(Qt::WidgetShortcut); menuExt.addAction( ContextMenuExtension::FileGroup, copy ); } if (needsPaste) { QAction* paste = KStandardAction::paste(this, SLOT(pasteFromContextMenu()), this); paste->setShortcutContext(Qt::WidgetShortcut); menuExt.addAction( ContextMenuExtension::FileGroup, paste ); } // Populating rename/remove group { menuExt.addAction( ContextMenuExtension::FileGroup, createSeparatorAction() ); } if ( needsCutRenameRemove ) { QAction* remove = new QAction(i18n("Remo&ve"), parent); remove->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); connect( remove, &QAction::triggered, this, &ProjectManagerViewPlugin::removeFromContextMenu ); menuExt.addAction( ContextMenuExtension::FileGroup, remove ); QAction* rename = new QAction(i18n("Re&name..."), parent); rename->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); connect( rename, &QAction::triggered, this, &ProjectManagerViewPlugin::renameItemFromContextMenu ); menuExt.addAction( ContextMenuExtension::FileGroup, rename ); } if ( needsRemoveTargetFiles ) { QAction* remove = new QAction(i18n("Remove From &Target"), parent); remove->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); connect( remove, &QAction::triggered, this, &ProjectManagerViewPlugin::removeTargetFilesFromContextMenu ); menuExt.addAction( ContextMenuExtension::FileGroup, remove ); } if ( needsCutRenameRemove || needsRemoveTargetFiles ) { menuExt.addAction(ContextMenuExtension::FileGroup, createSeparatorAction()); } return menuExt; } void ProjectManagerViewPlugin::closeProjects() { QList projectsToClose; ProjectModel* model = ICore::self()->projectController()->projectModel(); for (const QModelIndex& index : qAsConst(d->ctxProjectItemList)) { KDevelop::ProjectBaseItem* item = model->itemFromIndex(index); if( !projectsToClose.contains( item->project() ) ) { projectsToClose << item->project(); } } d->ctxProjectItemList.clear(); for (KDevelop::IProject* proj : qAsConst(projectsToClose)) { core()->projectController()->closeProject( proj ); } } void ProjectManagerViewPlugin::installItemsFromContextMenu() { runBuilderJob( BuilderJob::Install, itemsFromIndexes(d->ctxProjectItemList) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::cleanItemsFromContextMenu() { runBuilderJob( BuilderJob::Clean, itemsFromIndexes( d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::buildItemsFromContextMenu() { runBuilderJob( BuilderJob::Build, itemsFromIndexes( d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } QList ProjectManagerViewPlugin::collectAllProjects() { QList items; const auto projects = core()->projectController()->projects(); items.reserve(projects.size()); for (auto* project : projects) { items << project->projectItem(); } return items; } void ProjectManagerViewPlugin::buildAllProjects() { runBuilderJob( BuilderJob::Build, collectAllProjects() ); } QList ProjectManagerViewPlugin::collectItems() { QList items; const QList buildItems = ICore::self()->projectController()->buildSetModel()->items(); if( !buildItems.isEmpty() ) { for (const BuildItem& buildItem : buildItems) { if( ProjectBaseItem* item = buildItem.findItem() ) { items << item; } } } else { auto* ctx = static_cast(ICore::self()->selectionController()->currentSelection()); items = ctx->items(); } return items; } void ProjectManagerViewPlugin::runBuilderJob( BuilderJob::BuildType type, const QList& items ) { auto* builder = new BuilderJob; builder->addItems( type, items ); builder->updateJobName(); ICore::self()->uiController()->registerStatus(new JobStatus(builder)); ICore::self()->runController()->registerJob( builder ); } void ProjectManagerViewPlugin::installProjectItems() { runBuilderJob( KDevelop::BuilderJob::Install, collectItems() ); } void ProjectManagerViewPlugin::pruneProjectItems() { runBuilderJob( KDevelop::BuilderJob::Prune, collectItems() ); } void ProjectManagerViewPlugin::configureProjectItems() { runBuilderJob( KDevelop::BuilderJob::Configure, collectItems() ); } void ProjectManagerViewPlugin::cleanProjectItems() { runBuilderJob( KDevelop::BuilderJob::Clean, collectItems() ); } void ProjectManagerViewPlugin::buildProjectItems() { runBuilderJob( KDevelop::BuilderJob::Build, collectItems() ); } void ProjectManagerViewPlugin::addItemsFromContextMenuToBuildset( ) { const auto items = itemsFromIndexes(d->ctxProjectItemList); for (KDevelop::ProjectBaseItem* item : items) { ICore::self()->projectController()->buildSetModel()->addProjectItem( item ); } } void ProjectManagerViewPlugin::runTargetsFromContextMenu( ) { const auto items = itemsFromIndexes(d->ctxProjectItemList); for (KDevelop::ProjectBaseItem* item : items) { KDevelop::ProjectExecutableTargetItem* t=item->executable(); if(t) { qCDebug(PLUGIN_PROJECTMANAGERVIEW) << "Running target: " << t->text() << t->builtUrl(); } } } void ProjectManagerViewPlugin::projectConfiguration( ) { if( !d->ctxProjectItemList.isEmpty() ) { ProjectModel* model = ICore::self()->projectController()->projectModel(); core()->projectController()->configureProject( model->itemFromIndex(d->ctxProjectItemList.at( 0 ))->project() ); } } void ProjectManagerViewPlugin::reloadFromContextMenu( ) { QList< KDevelop::ProjectFolderItem* > folders; const auto items = itemsFromIndexes(d->ctxProjectItemList); for (KDevelop::ProjectBaseItem* item : items) { if ( item->folder() ) { // since reloading should be recursive, only pass the upper-most items bool found = false; const auto currentFolders = folders; for (KDevelop::ProjectFolderItem* existing : currentFolders) { if ( existing->path().isParentOf(item->folder()->path()) ) { // simply skip this child found = true; break; } else if ( item->folder()->path().isParentOf(existing->path()) ) { // remove the child in the list and add the current item instead folders.removeOne(existing); // continue since there could be more than one existing child } } if ( !found ) { folders << item->folder(); } } } for (KDevelop::ProjectFolderItem* folder : qAsConst(folders)) { folder->project()->projectFileManager()->reload(folder); } } void ProjectManagerViewPlugin::createFolderFromContextMenu( ) { const auto items = itemsFromIndexes(d->ctxProjectItemList); for (KDevelop::ProjectBaseItem* item : items) { if ( item->folder() ) { QWidget* window(ICore::self()->uiController()->activeMainWindow()->window()); QString name = QInputDialog::getText ( window, i18n ( "Create Folder in %1", item->folder()->path().pathOrUrl() ), i18n ( "Folder name:" ) ); if (!name.isEmpty()) { item->project()->projectFileManager()->addFolder( Path(item->path(), name), item->folder() ); } } } } void ProjectManagerViewPlugin::removeFromContextMenu() { removeItems(itemsFromIndexes( d->ctxProjectItemList )); } void ProjectManagerViewPlugin::removeItems(const QList< ProjectBaseItem* >& items) { if (items.isEmpty()) { return; } //copy the list of selected items and sort it to guarantee parents will come before children QList sortedItems = items; std::sort(sortedItems.begin(), sortedItems.end(), ProjectBaseItem::pathLessThan); Path lastFolder; QHash< IProjectFileManager*, QList > filteredItems; QStringList itemPaths; for (KDevelop::ProjectBaseItem* item : qAsConst(sortedItems)) { if (item->isProjectRoot()) { continue; } else if (item->folder() || item->file()) { //make sure no children of folders that will be deleted are listed if (lastFolder.isParentOf(item->path())) { continue; } else if (item->folder()) { lastFolder = item->path(); } IProjectFileManager* manager = item->project()->projectFileManager(); if (manager) { filteredItems[manager] << item; itemPaths << item->path().pathOrUrl(); } } } if (filteredItems.isEmpty()) { return; } if (KMessageBox::warningYesNoList( QApplication::activeWindow(), i18np("Do you really want to delete this item?", "Do you really want to delete these %1 items?", itemPaths.size()), itemPaths, i18n("Delete Files"), KStandardGuiItem::del(), KStandardGuiItem::cancel() ) == KMessageBox::No) { return; } //Go though projectmanagers, have them remove the files and folders that they own QHash< IProjectFileManager*, QList >::iterator it; for (it = filteredItems.begin(); it != filteredItems.end(); ++it) { Q_ASSERT(it.key()); it.key()->removeFilesAndFolders(it.value()); } } void ProjectManagerViewPlugin::removeTargetFilesFromContextMenu() { const QList items = itemsFromIndexes( d->ctxProjectItemList ); QHash< IBuildSystemManager*, QList > itemsByBuildSystem; for (ProjectBaseItem* item : items) { itemsByBuildSystem[item->project()->buildSystemManager()].append(item->file()); } QHash< IBuildSystemManager*, QList >::iterator it; for (it = itemsByBuildSystem.begin(); it != itemsByBuildSystem.end(); ++it) it.key()->removeFilesFromTargets(it.value()); } void ProjectManagerViewPlugin::renameItemFromContextMenu() { renameItems(itemsFromIndexes( d->ctxProjectItemList )); } void ProjectManagerViewPlugin::renameItems(const QList< ProjectBaseItem* >& items) { if (items.isEmpty()) { return; } QWidget* window = ICore::self()->uiController()->activeMainWindow()->window(); for (KDevelop::ProjectBaseItem* item : items) { if ((item->type()!=ProjectBaseItem::BuildFolder && item->type()!=ProjectBaseItem::Folder && item->type()!=ProjectBaseItem::File) || !item->parent()) { continue; } const QString src = item->text(); //Change QInputDialog->KFileSaveDialog? QString name = QInputDialog::getText( window, i18n("Rename..."), i18n("New name for '%1':", item->text()), QLineEdit::Normal, item->text() ); if (!name.isEmpty() && name != src) { ProjectBaseItem::RenameStatus status = item->rename( name ); QString errorMessageText; switch(status) { case ProjectBaseItem::RenameOk: break; case ProjectBaseItem::ExistingItemSameName: errorMessageText = i18n("There is already a file named '%1'", name); break; case ProjectBaseItem::ProjectManagerRenameFailed: errorMessageText = i18n("Could not rename '%1'", name); break; case ProjectBaseItem::InvalidNewName: errorMessageText = i18n("'%1' is not a valid file name", name); break; } if (!errorMessageText.isEmpty()) { auto* message = new Sublime::Message(errorMessageText, Sublime::Message::Error); ICore::self()->uiController()->postMessage(message); } } } } ProjectFileItem* createFile(const ProjectFolderItem* item) { QWidget* window = ICore::self()->uiController()->activeMainWindow()->window(); QString name = QInputDialog::getText(window, i18n("Create File in %1", item->path().pathOrUrl()), i18n("File name:")); if(name.isEmpty()) return nullptr; ProjectFileItem* ret = item->project()->projectFileManager()->addFile( Path(item->path(), name), item->folder() ); if (ret) { ICore::self()->documentController()->openDocument( ret->path().toUrl() ); } return ret; } void ProjectManagerViewPlugin::createFileFromContextMenu( ) { const auto items = itemsFromIndexes(d->ctxProjectItemList); for (KDevelop::ProjectBaseItem* item : items) { if ( item->folder() ) { createFile(item->folder()); } else if ( item->target() ) { auto* folder=dynamic_cast(item->parent()); if(folder) { ProjectFileItem* f=createFile(folder); if(f) item->project()->buildSystemManager()->addFilesToTarget(QList() << f, item->target()); } } } } void ProjectManagerViewPlugin::copyFromContextMenu() { qApp->clipboard()->setMimeData(createClipboardMimeData(false)); } void ProjectManagerViewPlugin::cutFromContextMenu() { qApp->clipboard()->setMimeData(createClipboardMimeData(true)); } static void selectItemsByPaths(ProjectManagerView* view, const Path::List& paths) { KDevelop::ProjectModel* projectModel = KDevelop::ICore::self()->projectController()->projectModel(); QList newItems; for (const Path& path : paths) { QList items = projectModel->itemsForPath(IndexedString(path.path())); newItems.append(items); for (ProjectBaseItem* item : qAsConst(items)) { view->expandItem(item->parent()); } } view->selectItems(newItems); } void ProjectManagerViewPlugin::pasteFromContextMenu() { auto* ctx = static_cast(ICore::self()->selectionController()->currentSelection()); if (ctx->items().count() != 1) { return; //do nothing if multiple or none items are selected } ProjectBaseItem* destItem = ctx->items().at(0); if (!destItem->folder()) { return; //do nothing if the target is not a directory } const QMimeData* data = qApp->clipboard()->mimeData(); qCDebug(PLUGIN_PROJECTMANAGERVIEW) << data->urls(); Path::List origPaths = toPathList(data->urls()); const bool isCut = KIO::isClipboardDataCut(data); const CutCopyPasteHelpers::SourceToDestinationMap map = CutCopyPasteHelpers::mapSourceToDestination(origPaths, destItem->folder()->path()); - QVector tasks = CutCopyPasteHelpers::copyMoveItems( + const QVector tasks = CutCopyPasteHelpers::copyMoveItems( map.filteredPaths, destItem, isCut ? CutCopyPasteHelpers::Operation::CUT : CutCopyPasteHelpers::Operation::COPY); // Select new items in the project manager view auto* itemCtx = dynamic_cast(ICore::self()->selectionController()->currentSelection()); if (itemCtx) { Path::List finalPathsList; for (const auto& task : tasks) { if (task.m_status == CutCopyPasteHelpers::TaskStatus::SUCCESS && task.m_type != CutCopyPasteHelpers::TaskType::DELETION) { finalPathsList.reserve(finalPathsList.size() + task.m_src.size()); for (const Path& src : task.m_src) { finalPathsList.append(map.finalPaths[src]); } } } selectItemsByPaths(itemCtx->view(), finalPathsList); } // If there was a single failure, display a warning dialog. const bool anyFailed = std::any_of(tasks.begin(), tasks.end(), [](const CutCopyPasteHelpers::TaskInfo& task) { return task.m_status != CutCopyPasteHelpers::TaskStatus::SUCCESS; }); if (anyFailed) { QWidget* window = ICore::self()->uiController()->activeMainWindow()->window(); showWarningDialogForFailedPaste(window, tasks); } } #include "projectmanagerviewplugin.moc" diff --git a/plugins/projectmanagerview/projecttreeview.cpp b/plugins/projectmanagerview/projecttreeview.cpp index a5ea7115ac..bbf1e40e77 100644 --- a/plugins/projectmanagerview/projecttreeview.cpp +++ b/plugins/projectmanagerview/projecttreeview.cpp @@ -1,480 +1,480 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2009 Aleix Pol This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projecttreeview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "projectmanagerviewplugin.h" #include "projectmodelsaver.h" #include "projectmodelitemdelegate.h" #include "debug.h" #include #include using namespace KDevelop; namespace { QString settingsConfigGroup() { return QStringLiteral("ProjectTreeView"); } QList fileItemsWithin(const QList& items) { QList fileItems; fileItems.reserve(items.size()); for (ProjectBaseItem* item : items) { if (ProjectFileItem *file = item->file()) fileItems.append(file); else if (item->folder()) fileItems.append(fileItemsWithin(item->children())); } return fileItems; } QList topLevelItemsWithin(QList items) { std::sort(items.begin(), items.end(), ProjectBaseItem::pathLessThan); Path lastFolder; for (int i = items.size() - 1; i >= 0; --i) { if (lastFolder.isParentOf(items[i]->path())) items.removeAt(i); else if (items[i]->folder()) lastFolder = items[i]->path(); } return items; } template void filterDroppedItems(QList &items, ProjectBaseItem* dest) { for (int i = items.size() - 1; i >= 0; --i) { //No drag and drop from and to same location if (items[i]->parent() == dest) items.removeAt(i); //No moving between projects (technically feasible if the projectmanager is the same though...) else if (items[i]->project() != dest->project()) items.removeAt(i); } } //TODO test whether this could be replaced by projectbuildsetwidget.cpp::showContextMenu_appendActions void popupContextMenu_appendActions(QMenu& menu, const QList& actions) { menu.addActions(actions); menu.addSeparator(); } } ProjectTreeView::ProjectTreeView( QWidget *parent ) : QTreeView( parent ), m_previousSelection ( nullptr ) { header()->hide(); setEditTriggers( QAbstractItemView::EditKeyPressed ); setContextMenuPolicy( Qt::CustomContextMenu ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setIndentation(10); setDragEnabled(true); setDragDropMode(QAbstractItemView::InternalMove); setAutoScroll(true); setAutoExpandDelay(300); setItemDelegate(new ProjectModelItemDelegate(this)); connect( this, &ProjectTreeView::customContextMenuRequested, this, &ProjectTreeView::popupContextMenu ); connect( this, &ProjectTreeView::activated, this, &ProjectTreeView::slotActivated ); connect( ICore::self(), &ICore::aboutToShutdown, this, &ProjectTreeView::aboutToShutdown); connect( ICore::self()->projectController(), &IProjectController::projectOpened, this, &ProjectTreeView::restoreState ); connect( ICore::self()->projectController(), &IProjectController::projectClosed, this, &ProjectTreeView::projectClosed ); } ProjectTreeView::~ProjectTreeView() { } ProjectBaseItem* ProjectTreeView::itemAtPos(const QPoint& pos) const { return indexAt(pos).data(ProjectModel::ProjectItemRole).value(); } void ProjectTreeView::dropEvent(QDropEvent* event) { auto* selectionCtxt = static_cast(KDevelop::ICore::self()->selectionController()->currentSelection()); ProjectBaseItem* destItem = itemAtPos(event->pos()); if (destItem && (dropIndicatorPosition() == AboveItem || dropIndicatorPosition() == BelowItem)) destItem = destItem->parent(); if (selectionCtxt && destItem) { if (ProjectFolderItem *folder = destItem->folder()) { QMenu dropMenu(this); QString seq = QKeySequence( Qt::ShiftModifier ).toString(); seq.chop(1); // chop superfluous '+' QAction* move = new QAction(i18n("&Move Here") + QLatin1Char('\t') + seq, &dropMenu); move->setIcon(QIcon::fromTheme(QStringLiteral("go-jump"))); dropMenu.addAction(move); seq = QKeySequence( Qt::ControlModifier ).toString(); seq.chop(1); QAction* copy = new QAction(i18n("&Copy Here") + QLatin1Char('\t') + seq, &dropMenu); copy->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); dropMenu.addAction(copy); dropMenu.addSeparator(); QAction* cancel = new QAction(i18n("C&ancel") + QLatin1Char('\t') + QKeySequence(Qt::Key_Escape).toString(), &dropMenu); cancel->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); dropMenu.addAction(cancel); QAction *executedAction = nullptr; Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); if (modifiers == Qt::ControlModifier) { executedAction = copy; } else if (modifiers == Qt::ShiftModifier) { executedAction = move; } else { executedAction = dropMenu.exec(this->mapToGlobal(event->pos())); } QList usefulItems = topLevelItemsWithin(selectionCtxt->items()); filterDroppedItems(usefulItems, destItem); Path::List paths; paths.reserve(usefulItems.size()); for (ProjectBaseItem* i : qAsConst(usefulItems)) { paths << i->path(); } bool success = false; if (executedAction == copy) { success = destItem->project()->projectFileManager()->copyFilesAndFolders(paths, folder); } else if (executedAction == move) { success = destItem->project()->projectFileManager()->moveFilesAndFolders(usefulItems, folder); } if (success) { //expand target folder expand( mapFromItem(folder)); //and select new items QItemSelection selection; for (const Path& path : qAsConst(paths)) { const Path targetPath(folder->path(), path.lastPathSegment()); const auto folderChildren = folder->children(); for (ProjectBaseItem* item : folderChildren) { if (item->path() == targetPath) { QModelIndex indx = mapFromItem( item ); selection.append(QItemSelectionRange(indx, indx)); setCurrentIndex(indx); } } } selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); } } else if (destItem->target() && destItem->project()->buildSystemManager()) { QMenu dropMenu(this); QString seq = QKeySequence( Qt::ControlModifier ).toString(); seq.chop(1); QAction* addToTarget = new QAction(i18n("&Add to Target") + QLatin1Char('\t') + seq, &dropMenu); addToTarget->setIcon(QIcon::fromTheme(QStringLiteral("edit-link"))); dropMenu.addAction(addToTarget); dropMenu.addSeparator(); QAction* cancel = new QAction(i18n("C&ancel") + QLatin1Char('\t') + QKeySequence(Qt::Key_Escape).toString(), &dropMenu); cancel->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); dropMenu.addAction(cancel); QAction *executedAction = nullptr; Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); if (modifiers == Qt::ControlModifier) { executedAction = addToTarget; } else { executedAction = dropMenu.exec(this->mapToGlobal(event->pos())); } if (executedAction == addToTarget) { QList usefulItems = fileItemsWithin(selectionCtxt->items()); filterDroppedItems(usefulItems, destItem); destItem->project()->buildSystemManager()->addFilesToTarget(usefulItems, destItem->target()); } } } event->accept(); } QModelIndex ProjectTreeView::mapFromSource(const QAbstractProxyModel* proxy, const QModelIndex& sourceIdx) { const QAbstractItemModel* next = proxy->sourceModel(); Q_ASSERT(next == sourceIdx.model() || qobject_cast(next)); if(next == sourceIdx.model()) return proxy->mapFromSource(sourceIdx); else { const auto* nextProxy = qobject_cast(next); QModelIndex idx = mapFromSource(nextProxy, sourceIdx); Q_ASSERT(idx.model() == nextProxy); return proxy->mapFromSource(idx); } } QModelIndex ProjectTreeView::mapFromItem(const ProjectBaseItem* item) { QModelIndex ret = mapFromSource(qobject_cast(model()), item->index()); Q_ASSERT(ret.model() == model()); return ret; } void ProjectTreeView::slotActivated( const QModelIndex &index ) { if ( QApplication::keyboardModifiers() & Qt::CTRL || QApplication::keyboardModifiers() & Qt::SHIFT ) { // Do not open file when Ctrl or Shift is pressed; that's for selection return; } auto *item = index.data(ProjectModel::ProjectItemRole).value(); if ( item && item->file() ) { emit activate( item->file()->path() ); } } void ProjectTreeView::projectClosed(KDevelop::IProject* project) { if ( project == m_previousSelection ) m_previousSelection = nullptr; } QList ProjectTreeView::selectedProjects() { QList itemlist; if ( selectionModel()->hasSelection() ) { - QModelIndexList indexes = selectionModel()->selectedRows(); + const QModelIndexList indexes = selectionModel()->selectedRows(); for ( const QModelIndex& index: indexes ) { auto* item = index.data( ProjectModel::ProjectItemRole ).value(); if ( item ) { itemlist << item; m_previousSelection = item->project(); } } } // add previous selection if nothing is selected right now if ( itemlist.isEmpty() && m_previousSelection ) { itemlist << m_previousSelection->projectItem(); } return itemlist; } KDevelop::IProject* ProjectTreeView::getCurrentProject() { auto itemList = selectedProjects(); if ( !itemList.isEmpty() ) { return itemList.at( 0 )->project(); } return nullptr; } void ProjectTreeView::popupContextMenu( const QPoint &pos ) { QList itemlist; if ( indexAt( pos ).isValid() ) { itemlist = selectedProjects(); } QMenu menu( this ); KDevelop::ProjectItemContextImpl context(itemlist); const QList extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions(&context, &menu); QList buildActions; QList vcsActions; QList analyzeActions; QList extActions; QList projectActions; QList fileActions; QList runActions; for (const ContextMenuExtension& ext : extensions) { buildActions += ext.actions(ContextMenuExtension::BuildGroup); fileActions += ext.actions(ContextMenuExtension::FileGroup); projectActions += ext.actions(ContextMenuExtension::ProjectGroup); vcsActions += ext.actions(ContextMenuExtension::VcsGroup); analyzeActions += ext.actions(ContextMenuExtension::AnalyzeProjectGroup); extActions += ext.actions(ContextMenuExtension::ExtensionGroup); runActions += ext.actions(ContextMenuExtension::RunGroup); } if ( analyzeActions.count() ) { QMenu* analyzeMenu = new QMenu(i18n("Analyze With"), &menu); analyzeMenu->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok"))); for (QAction* act : qAsConst(analyzeActions)) { analyzeMenu->addAction( act ); } analyzeActions = {analyzeMenu->menuAction()}; } popupContextMenu_appendActions(menu, buildActions); popupContextMenu_appendActions(menu, runActions ); popupContextMenu_appendActions(menu, fileActions); popupContextMenu_appendActions(menu, vcsActions); popupContextMenu_appendActions(menu, analyzeActions); popupContextMenu_appendActions(menu, extActions); if (itemlist.size() == 1 && itemlist.first()->folder() && !itemlist.first()->folder()->parent()) { QAction* projectConfig = new QAction(i18n("Open Configuration..."), &menu); projectConfig->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); connect( projectConfig, &QAction::triggered, this, &ProjectTreeView::openProjectConfig ); projectActions << projectConfig; } popupContextMenu_appendActions(menu, projectActions); if ( !menu.isEmpty() ) { menu.exec(viewport()->mapToGlobal(pos)); } } void ProjectTreeView::openProjectConfig() { if ( IProject* project = getCurrentProject() ) { IProjectController* ip = ICore::self()->projectController(); ip->configureProject( project ); } } void ProjectTreeView::saveState( IProject* project ) { // nullptr won't create a usable saved state, so spare the effort if ( !project ) { return; } KConfigGroup configGroup( ICore::self()->activeSession()->config(), settingsConfigGroup() + project->name() ); ProjectModelSaver saver; saver.setProject( project ); saver.setView( this ); saver.saveState( configGroup ); } void ProjectTreeView::restoreState( IProject* project ) { if ( !project ) { return; } KConfigGroup configGroup( ICore::self()->activeSession()->config(), settingsConfigGroup() + project->name() ); ProjectModelSaver saver; saver.setProject( project ); saver.setView( this ); saver.restoreState( configGroup ); } void ProjectTreeView::rowsInserted( const QModelIndex& parent, int start, int end ) { QTreeView::rowsInserted( parent, start, end ); if ( !parent.model() ) { const auto& projects = selectedProjects(); for (const auto& project: projects) { restoreState( project->project() ); } } } void ProjectTreeView::rowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { if ( !parent.model() ) { const auto& projects = selectedProjects(); for (const auto& project : projects) { saveState( project->project() ); } } QTreeView::rowsAboutToBeRemoved( parent, start, end ); } void ProjectTreeView::aboutToShutdown() { // save all projects, not just the selected ones const auto projects = ICore::self()->projectController()->projects(); for ( const auto& project: projects ) { saveState( project ); } } void ProjectTreeView::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Return && currentIndex().isValid() && state()!=QAbstractItemView::EditingState) { event->accept(); slotActivated(currentIndex()); } else QTreeView::keyPressEvent(event); } void ProjectTreeView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const { if (WidgetColorizer::colorizeByProject()) { const auto projectPath = index.data(ProjectModel::ProjectRole).value()->path(); const QColor color = WidgetColorizer::colorForId(qHash(projectPath), palette(), true); WidgetColorizer::drawBranches(this, painter, rect, index, color); } QTreeView::drawBranches(painter, rect, index); } diff --git a/plugins/qmakebuilder/kdevqmakebuilder.json b/plugins/qmakebuilder/kdevqmakebuilder.json index 5d3edaad93..b8be8e1096 100644 --- a/plugins/qmakebuilder/kdevqmakebuilder.json +++ b/plugins/qmakebuilder/kdevqmakebuilder.json @@ -1,68 +1,66 @@ { "KPlugin": { "Category": "Project Management", "Description": "Builds QMake Projects", "Description[ca@valencia]": "Construeix projectes QMake", "Description[ca]": "Construeix projectes QMake", "Description[cs]": "Překládá projekty QMake", "Description[de]": "Erstellt QMake-Projekte", - "Description[el]": "Κατασκευάζει έργα qmake", "Description[en_GB]": "Builds QMake Projects", "Description[es]": "Compila proyectos de QMake", "Description[et]": "QMake'i projektide ehitamine", "Description[fi]": "Kääntää QMake-projekteja", "Description[fr]": "Construit des projets QMake", "Description[gl]": "Constrúe proxectos QMake.", "Description[it]": "Compila i progetti QMake", "Description[nl]": "Bouwt QMake-projecten", "Description[pl]": "Buduje projekty QMake", "Description[pt]": "Cria projectos do QMake", "Description[pt_BR]": "Compila projetos do QMake", "Description[sk]": "Prekladá projekty QMake", "Description[sl]": "Izgradi projekte QMake", "Description[sv]": "Bygger QMake-projekt", "Description[tr]": "QMake Projelerini Derler", "Description[uk]": "Збирає проєкти QMake", "Description[x-test]": "xxBuilds QMake Projectsxx", "Description[zh_CN]": "构建 QMake 工程", "Icon": "qtlogo", "Id": "KDevQMakeBuilder", "Name": "QMake Project Builder", "Name[ca@valencia]": "Constructor de projecte QMake", "Name[ca]": "Constructor de projecte QMake", "Name[cs]": "Překladač projektů QMake", "Name[de]": "Ersteller für QMake-Projekte", - "Name[el]": "Κατασκευαστής έργου QMake", "Name[en_GB]": "QMake Project Builder", "Name[es]": "Compilador de proyectos QMake", - "Name[et]": "QMake'i projektiehitaja", + "Name[et]": "QMake'i projekti ehitaja", "Name[fi]": "QMake-projektikäännin", "Name[fr]": "Constructeur de projet QMake", "Name[gl]": "Construtor de proxectos QMake", "Name[it]": "Compilatore progetto QMake", "Name[nl]": "QMake-projectbouwer", "Name[pl]": "Budowniczy projektu QMake", "Name[pt]": "Compilador de Projectos do QMake", "Name[pt_BR]": "Construtor de projetos do QMake", "Name[sk]": "Prekladač projektov QMake", "Name[sl]": "Izgrajevalnik projektov QMake", "Name[sv]": "QMake-projektbyggverktyg", "Name[tr]": "QMake Proje Oluşturucu", "Name[uk]": "Засіб збирання проєктів QMake", "Name[x-test]": "xxQMake Project Builderxx", "Name[zh_CN]": "QMake 工程构建器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView", "org.kdevelop.IMakeBuilder" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IQMakeBuilder" ], "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectBuilder": "QMake" } diff --git a/plugins/qmakemanager/kdevqmakemanager.json b/plugins/qmakemanager/kdevqmakemanager.json index b729cce4ef..95a0ffc0da 100644 --- a/plugins/qmakemanager/kdevqmakemanager.json +++ b/plugins/qmakemanager/kdevqmakemanager.json @@ -1,72 +1,70 @@ { "KPlugin": { "Category": "Project Management", "Description": "Imports and edits QMake projects", "Description[ca@valencia]": "Importa i edita projectes del QMake", "Description[ca]": "Importa i edita projectes del QMake", "Description[cs]": "Importuje a upravuje vlastní projekty QMake", "Description[de]": "Import und Bearbeitung von benutzerdefinierten QMake-Projekten", - "Description[el]": "Εισάγει και επεξεργάζεται έργα qmake", "Description[en_GB]": "Imports and edits QMake projects", "Description[es]": "Importa y edita proyectos QMake", - "Description[et]": "QMake'i projektide importimine ja muutmine", + "Description[et]": "QMake'i projektide import ja muutmine", "Description[fi]": "Tuo ja muokkaa QMake-projekteja", "Description[fr]": "Importe et édite des projets QMake", "Description[gl]": "Importa e edita proxectos QMake.", "Description[it]": "Importa e modifica i progetti QMake", "Description[nl]": "Importeert en bewerkt QMake-projecten", "Description[pl]": "Importuje i edytuje projekty QMake", "Description[pt]": "Importa e edita os projectos do QMake", "Description[pt_BR]": "Importa e edita os projetos do QMake", "Description[sk]": "Importuje a upravuje projekty QMake", "Description[sl]": "Uvozi in ureja projekte po meri temelječe na QMake", "Description[sv]": "Importerar och redigerar QMake-projekt", "Description[tr]": "QMake projelerini içeriye aktarır ve düzenler", "Description[uk]": "Імпортує і дає змогу редагувати проєкти QMake", "Description[x-test]": "xxImports and edits QMake projectsxx", "Description[zh_CN]": "导入并编辑 QMake 工程", "Icon": "qtlogo", "Id": "KDevQMakeManager", "Name": "QMake Project Manager", "Name[ca@valencia]": "Gestor de projectes QMake", "Name[ca]": "Gestor de projectes QMake", "Name[cs]": "Správce projektů QMake", "Name[de]": "QMake-Projektverwaltung", - "Name[el]": "Διαχειριστής έργου QMake", "Name[en_GB]": "QMake Project Manager", "Name[es]": "Gestor de proyectos QMake", "Name[et]": "QMake'i projektihaldur", "Name[fi]": "QMake-projektinhallinta", "Name[fr]": "Gestionnaire de projet QMake", "Name[gl]": "Xestor de proxectos QMake", "Name[it]": "Gestore progetto QMake", "Name[nl]": "QMake-projectbeheerder", "Name[pl]": "Zarządzanie projektami QMake", - "Name[pt]": "Gestor de Projectos do QMake", + "Name[pt]": "Gestor de Projectos QMake", "Name[pt_BR]": "Gerenciador de projetos do QMake", "Name[sk]": "Správca projektov QMake", "Name[sl]": "Upravljalnik projektov QMake", "Name[sv]": "QMake-projekthantering", "Name[tr]": "QMake Proje Yöneticisi", "Name[uk]": "Керування проєктами QMake", "Name[x-test]": "xxQMake Project Managerxx", "Name[zh_CN]": "QMake 工程管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-FileManager": "QMake", "X-KDevelop-IRequired": [ "org.kdevelop.IQMakeBuilder" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBuildSystemManager", "org.kdevelop.IProjectFileManager" ], "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectFilesFilter": [ "*.pro" ], "X-KDevelop-ProjectFilesFilterDescription": "QMake Project Files" } diff --git a/plugins/qmljs/3rdparty/qtcreator-libs/CMakeLists.txt b/plugins/qmljs/3rdparty/qtcreator-libs/CMakeLists.txt index 5069a3a2da..52680e24d7 100644 --- a/plugins/qmljs/3rdparty/qtcreator-libs/CMakeLists.txt +++ b/plugins/qmljs/3rdparty/qtcreator-libs/CMakeLists.txt @@ -1,94 +1,95 @@ remove_definitions( -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_FOREACH + -DQT_DEPRECATED_WARNINGS_SINCE=0x060000 ) qt5_wrap_ui(uifiles_SRCS utils/projectintropage.ui utils/filewizardpage.ui utils/newclasswidget.ui ) add_library(kdevqtc-qmlsupport STATIC ${uifiles_SRCS} languageutils/componentversion.cpp languageutils/fakemetaobject.cpp qmljs/parser/qmldirparser.cpp qmljs/parser/qmlerror.cpp qmljs/parser/qmljsast.cpp qmljs/parser/qmljsastvisitor.cpp qmljs/parser/qmljsengine_p.cpp qmljs/parser/qmljsgrammar.cpp qmljs/parser/qmljslexer.cpp qmljs/parser/qmljsparser.cpp qmljs/qmljsdocument.cpp qmljs/qmljsutils.cpp utils/changeset.cpp utils/fileutils.cpp utils/qtcassert.cpp utils/savefile.cpp utils/json.cpp utils/filesystemwatcher.cpp utils/environment.cpp utils/hostosinfo.cpp utils/runextensions.cpp qmljs/persistenttrie.cpp qmljs/qmljsbind.cpp qmljs/qmljsbundle.cpp qmljs/qmljscontext.cpp qmljs/qmljsdialect.cpp qmljs/qmljsevaluate.cpp qmljs/qmljsimportdependencies.cpp qmljs/qmljsinterpreter.cpp qmljs/qmljsmodelmanagerinterface.cpp qmljs/qmljsplugindumper.cpp qmljs/qmljsqrcparser.cpp qmljs/qmljsscopeastpath.cpp qmljs/qmljsscopebuilder.cpp qmljs/qmljsscopechain.cpp qmljs/qmljstypedescriptionreader.cpp qmljs/qmljsvalueowner.cpp qmljs/qmljsviewercontext.cpp ) if (APPLE) target_sources(kdevqtc-qmlsupport PRIVATE utils/fileutils_mac.mm) target_link_libraries(kdevqtc-qmlsupport PRIVATE "-framework Foundation" ) endif() # silence warnings which won't be fixed in this 3rd-party code copy, to keep diff small if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-documentation") endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-pedantic") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-implicit-fallthrough") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-overloaded-virtual") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-zero-as-null-pointer-constant") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-deprecated-declarations") endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-suggest-override") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-class-memaccess") add_target_compile_flag_if_supported(kdevqtc-qmlsupport PRIVATE "-Wno-deprecated-copy") endif() target_compile_definitions(kdevqtc-qmlsupport PUBLIC -DLANGUAGEUTILS_LIBRARY -DUTILS_LIBRARY -DQT_CREATOR -DQML_BUILD_STATIC_LIB) # add as SYSTEM include dir so compiler does not emit warnings for the lib headers target_include_directories(kdevqtc-qmlsupport SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # optional.hpp needs -fexceptions # (otherwise: plugins/qmljs/3rdparty/qtcreator-libs/3rdparty/optional/optional.hpp:561:94: error: exception handling disabled, use -fexceptions to enable) kde_target_enable_exceptions(kdevqtc-qmlsupport PRIVATE) target_link_libraries(kdevqtc-qmlsupport PRIVATE Qt5::Widgets Qt5::Network Qt5::Xml ) set_target_properties(kdevqtc-qmlsupport PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES ) diff --git a/plugins/qmljs/codecompletion/items/functioncalltipcompletionitem.cpp b/plugins/qmljs/codecompletion/items/functioncalltipcompletionitem.cpp index d4adb6cc9e..be6aecf45a 100644 --- a/plugins/qmljs/codecompletion/items/functioncalltipcompletionitem.cpp +++ b/plugins/qmljs/codecompletion/items/functioncalltipcompletionitem.cpp @@ -1,182 +1,182 @@ /* * This file is part of qmljs, the QML/JS language support plugin for KDevelop * Copyright (c) 2014 Denis Steckelmacher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "functioncalltipcompletionitem.h" #include "../../duchain/helper.h" #include "../../duchain/functiontype.h" #include #include #include #include using namespace KDevelop; using namespace QmlJS; FunctionCalltipCompletionItem::FunctionCalltipCompletionItem(const DeclarationPointer& decl, int depth, int argumentIndex) : m_declaration(decl), m_depth(depth) { // Ensure that decl has a function type if (!decl) { return; } QmlJS::FunctionType::Ptr func = QmlJS::FunctionType::Ptr::dynamicCast(decl->abstractType()); if (!func) { return; } // Arguments can be fetch from the function declaration (if available), or // from its function type Declaration* funcDecl = func->declaration(decl->topContext()); DUContext* argsContext = (funcDecl ? funcDecl->internalContext() : nullptr); QStringList arguments; if (argsContext) { - auto args = argsContext->allDeclarations(CursorInRevision::invalid(), decl->topContext(), false); + const auto args = argsContext->allDeclarations(CursorInRevision::invalid(), decl->topContext(), false); arguments.reserve(args.size()); for (auto pair : args) { arguments.append(pair.first->toString()); } if (argumentIndex < args.count()) { m_currentArgumentType = args.at(argumentIndex).first->abstractType(); } } else { const auto args = func->arguments(); arguments.reserve(args.size()); for (auto type : args) { arguments.append(type->toString()); } if (argumentIndex < func->arguments().count()) { m_currentArgumentType = func->arguments().at(argumentIndex); } } // [type] functionName if (func->returnType()) { m_prefix = func->returnType()->toString() + QLatin1Char(' '); } m_prefix += decl->identifier().toString(); // (arg1, arg2, [currentArgument in m_currentArgument], arg4, arg5) m_arguments = QLatin1Char('('); for (int i=0; i 0) { m_arguments += QLatin1String(", "); } if (i == argumentIndex) { m_currentArgumentStart = m_arguments.length(); m_currentArgumentLength = arguments.at(i).length(); } m_arguments += arguments.at(i); } m_arguments += QLatin1Char(')'); } AbstractType::Ptr FunctionCalltipCompletionItem::currentArgumentType() const { return m_currentArgumentType; } QVariant FunctionCalltipCompletionItem::data(const QModelIndex& index, int role, const CodeCompletionModel* model) const { Q_UNUSED(model) switch (role) { case Qt::DisplayRole: switch (index.column()) { case CodeCompletionModel::Prefix: return m_prefix; case CodeCompletionModel::Arguments: return m_arguments; } break; case CodeCompletionModel::ArgumentHintDepth: return argumentHintDepth(); case CodeCompletionModel::CompletionRole: return (int)completionProperties(); case CodeCompletionModel::HighlightingMethod: if (index.column() == CodeCompletionModel::Arguments) { return (int)CodeCompletionModel::CustomHighlighting; } break; case CodeCompletionModel::CustomHighlight: if (index.column() == CodeCompletionModel::Arguments) { QTextFormat format; format.setBackground(QBrush(QColor::fromRgb(142, 186, 255))); // Same color as kdev-python format.setProperty(QTextFormat::FontWeight, 99); return QVariantList{ m_currentArgumentStart, m_currentArgumentLength, format, }; } break; case Qt::DecorationRole: if (index.column() == CodeCompletionModel::Prefix) { return DUChainUtils::iconForProperties(completionProperties()); } break; } return QVariant(); } DeclarationPointer FunctionCalltipCompletionItem::declaration() const { return m_declaration; } int FunctionCalltipCompletionItem::argumentHintDepth() const { return m_depth; } int FunctionCalltipCompletionItem::inheritanceDepth() const { return 0; } CodeCompletionModel::CompletionProperties FunctionCalltipCompletionItem::completionProperties() const { return CodeCompletionModel::Function; } diff --git a/plugins/qmljs/duchain/declarationbuilder.cpp b/plugins/qmljs/duchain/declarationbuilder.cpp index dc3638880e..eb7e900a19 100644 --- a/plugins/qmljs/duchain/declarationbuilder.cpp +++ b/plugins/qmljs/duchain/declarationbuilder.cpp @@ -1,1542 +1,1542 @@ /************************************************************************************* * Copyright (C) 2012 by Aleix Pol * * Copyright (C) 2012 by Milian Wolff * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "declarationbuilder.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include "expressionvisitor.h" #include "parsesession.h" #include "functiondeclaration.h" #include "functiontype.h" #include "helper.h" #include "cache.h" #include "frameworks/nodejs.h" #include #include #include using namespace KDevelop; DeclarationBuilder::DeclarationBuilder(ParseSession* session) : m_prebuilding(false) { m_session = session; } ReferencedTopDUContext DeclarationBuilder::build(const IndexedString& url, QmlJS::AST::Node* node, const ReferencedTopDUContext& updateContext_) { Q_ASSERT(m_session->url() == url); ReferencedTopDUContext updateContext(updateContext_); // The declaration builder needs to run twice, so it can resolve uses of e.g. functions // which are called before they are defined (which is easily possible, due to JS's dynamic nature). if (!m_prebuilding) { qCDebug(KDEV_QMLJS_DUCHAIN) << "building, but running pre-builder first"; auto prebuilder = new DeclarationBuilder(m_session); prebuilder->m_prebuilding = true; updateContext = prebuilder->build(url, node, updateContext); qCDebug(KDEV_QMLJS_DUCHAIN) << "pre-builder finished"; delete prebuilder; if (!m_session->allDependenciesSatisfied()) { qCDebug(KDEV_QMLJS_DUCHAIN) << "dependencies were missing, don't perform the second parsing pass"; return updateContext; } } else { qCDebug(KDEV_QMLJS_DUCHAIN) << "prebuilding"; } return DeclarationBuilderBase::build(url, node, updateContext); } void DeclarationBuilder::startVisiting(QmlJS::AST::Node* node) { DUContext* builtinQmlContext = nullptr; if (QmlJS::isQmlFile(currentContext()) && !currentContext()->url().str().contains(QLatin1String("__builtin_qml.qml"))) { builtinQmlContext = m_session->contextOfFile( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevqmljssupport/nodejsmodules/__builtin_qml.qml")) ); } { DUChainWriteLocker lock; // Remove all the imported parent contexts: imports may have been edited // and there musn't be any leftover parent context currentContext()->topContext()->clearImportedParentContexts(); // Initialize Node.js QmlJS::NodeJS::instance().initialize(this); // Built-in QML types (color, rect, etc) if (builtinQmlContext) { topContext()->addImportedParentContext(builtinQmlContext); } } DeclarationBuilderBase::startVisiting(node); } /* * Functions */ template void DeclarationBuilder::declareFunction(QmlJS::AST::Node* node, bool newPrototypeContext, const Identifier& name, const RangeInRevision& nameRange, QmlJS::AST::Node* parameters, const RangeInRevision& parametersRange, QmlJS::AST::Node* body, const RangeInRevision& bodyRange) { setComment(node); // Declare the function QmlJS::FunctionType::Ptr func(new QmlJS::FunctionType); Decl* decl; { DUChainWriteLocker lock; decl = openDeclaration(name, nameRange); decl->setKind(Declaration::Type); func->setDeclaration(decl); decl->setType(func); } openType(func); // Parameters, if any (a function must always have an internal function context, // so always open a context here even if there are no parameters) DUContext* parametersContext = openContext( node + 1, // Don't call setContextOnNode on node, only the body context can be associated with node RangeInRevision(parametersRange.start, bodyRange.end), // Ensure that this context contains both the parameters and the body DUContext::Function, QualifiedIdentifier(name) ); if (parameters) { QmlJS::AST::Node::accept(parameters, this); } // The internal context of the function is its parameter context { DUChainWriteLocker lock; decl->setInternalContext(parametersContext); } // Open the prototype context, if any. This has to be done before the body // because this context is needed for "this" to be properly resolved // in it. if (newPrototypeContext) { DUChainWriteLocker lock; auto* d = reinterpret_cast(decl); d->setPrototypeContext(openContext( node + 2, // Don't call setContextOnNode on node, only the body context can be associated with node RangeInRevision(parametersRange.start, parametersRange.start), DUContext::Function, // This allows QmlJS::getOwnerOfContext to know that the parent of this context is the function declaration QualifiedIdentifier(name) )); if (name != Identifier(QStringLiteral("Object"))) { // Every class inherit from Object QmlJS::importObjectContext(currentContext(), topContext()); } closeContext(); } // Body, if any (it is a child context of the parameters) openContext( node, bodyRange, DUContext::Other, QualifiedIdentifier(name) ); if (body) { QmlJS::AST::Node::accept(body, this); } // Close the body context and then the parameters context closeContext(); closeContext(); } template void DeclarationBuilder::declareParameters(Node* node, QmlJS::AST::UiQualifiedId* Node::*typeFunc) { for (Node *plist = node; plist; plist = plist->next) { const Identifier name(plist->name.toString()); const RangeInRevision range = m_session->locationToRange(plist->identifierToken); AbstractType::Ptr type = (typeFunc ? typeFromName((plist->*typeFunc)->name.toString()) : // The typeAttribute attribute of plist contains the type name of the argument AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)) // No type information, use mixed ); { DUChainWriteLocker lock; openDeclaration(name, range); } openType(type); closeAndAssignType(); if (QmlJS::FunctionType::Ptr funType = currentType()) { funType->addArgument(type); } } } bool DeclarationBuilder::visit(QmlJS::AST::FunctionDeclaration* node) { declareFunction( node, true, // A function declaration always has its own prototype context Identifier(node->name.toString()), m_session->locationToRange(node->identifierToken), node->formals, m_session->locationsToRange(node->lparenToken, node->rparenToken), node->body, m_session->locationsToRange(node->lbraceToken, node->rbraceToken) ); return false; } bool DeclarationBuilder::visit(QmlJS::AST::FunctionExpression* node) { declareFunction( node, false, Identifier(), QmlJS::emptyRangeOnLine(node->functionToken), node->formals, m_session->locationsToRange(node->lparenToken, node->rparenToken), node->body, m_session->locationsToRange(node->lbraceToken, node->rbraceToken) ); return false; } bool DeclarationBuilder::visit(QmlJS::AST::FormalParameterList* node) { declareParameters(node, (QmlJS::AST::UiQualifiedId* QmlJS::AST::FormalParameterList::*)nullptr); return DeclarationBuilderBase::visit(node); } bool DeclarationBuilder::visit(QmlJS::AST::UiParameterList* node) { declareParameters(node, &QmlJS::AST::UiParameterList::type); return DeclarationBuilderBase::visit(node); } bool DeclarationBuilder::visit(QmlJS::AST::ReturnStatement* node) { if (QmlJS::FunctionType::Ptr func = currentType()) { AbstractType::Ptr returnType; if (node->expression) { returnType = findType(node->expression).type; } else { returnType = new IntegralType(IntegralType::TypeVoid); } DUChainWriteLocker lock; func->setReturnType(QmlJS::mergeTypes(func->returnType(), returnType)); } return false; // findType has already explored node } void DeclarationBuilder::endVisitFunction() { QmlJS::FunctionType::Ptr func = currentType(); if (func && !func->returnType()) { // A function that returns nothing returns void DUChainWriteLocker lock; func->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid))); } closeAndAssignType(); } void DeclarationBuilder::endVisit(QmlJS::AST::FunctionDeclaration* node) { DeclarationBuilderBase::endVisit(node); endVisitFunction(); } void DeclarationBuilder::endVisit(QmlJS::AST::FunctionExpression* node) { DeclarationBuilderBase::endVisit(node); endVisitFunction(); } /* * Variables */ void DeclarationBuilder::inferArgumentsFromCall(QmlJS::AST::Node* base, QmlJS::AST::ArgumentList* arguments) { ContextBuilder::ExpressionType expr = findType(base); QmlJS::FunctionType::Ptr func_type = QmlJS::FunctionType::Ptr::dynamicCast(expr.type); DUChainWriteLocker lock; if (!func_type) { return; } auto func_declaration = dynamic_cast(func_type->declaration(topContext())); if (!func_declaration || !func_declaration->internalContext()) { return; } // Put the argument nodes in a list that has a definite size QVector argumentDecls = func_declaration->internalContext()->localDeclarations(); QVector args; for (auto argument = arguments; argument; argument = argument->next) { args.append(argument); } // Don't update a function when it is called with the wrong number // of arguments if (args.size() != argumentDecls.count()) { return; } // Update the types of the function arguments QmlJS::FunctionType::Ptr new_func_type(new QmlJS::FunctionType); for (int i=0; iabstractType(); // Merge the current type of the argument with its type in the call expression AbstractType::Ptr call_type = findType(argument->expression).type; AbstractType::Ptr new_type = QmlJS::mergeTypes(current_type, call_type); // Update the declaration of the argument and its type in the function type if (func_declaration->topContext() == topContext()) { new_func_type->addArgument(new_type); argumentDecls.at(i)->setAbstractType(new_type); } // Add a warning if it is possible that the argument types don't match if (!m_prebuilding && !areTypesEqual(current_type, call_type)) { m_session->addProblem(argument, i18n( "Possible type mismatch between the argument type (%1) and the value passed as argument (%2)", current_type->toString(), call_type->toString() ), IProblem::Hint); } } // Replace the function's type with the new type having updated arguments if (func_declaration->topContext() == topContext()) { new_func_type->setReturnType(func_type->returnType()); new_func_type->setDeclaration(func_declaration); func_declaration->setAbstractType(new_func_type.cast()); if (expr.declaration) { // expr.declaration is the variable that contains the function, while // func_declaration is the declaration of the function. They can be // different and both need to be updated expr.declaration->setAbstractType(new_func_type.cast()); } } return; } bool DeclarationBuilder::visit(QmlJS::AST::VariableDeclaration* node) { setComment(m_session->commentForLocation(node->firstSourceLocation()).toUtf8()); const Identifier name(node->name.toString()); const RangeInRevision range = m_session->locationToRange(node->identifierToken); const AbstractType::Ptr type = findType(node->expression).type; { DUChainWriteLocker lock; openDeclaration(name, range); } openType(type); return false; // findType has already explored node } void DeclarationBuilder::endVisit(QmlJS::AST::VariableDeclaration* node) { DeclarationBuilderBase::endVisit(node); closeAndAssignType(); } bool DeclarationBuilder::visit(QmlJS::AST::BinaryExpression* node) { if (node->op == QSOperator::Assign) { ExpressionType leftType = findType(node->left); ExpressionType rightType = findType(node->right); DUChainWriteLocker lock; if (leftType.declaration) { DUContext* leftCtx = leftType.declaration->context(); DUContext* leftInternalCtx = QmlJS::getInternalContext(leftType.declaration); // object.prototype.method = function(){} : when assigning a function // to a variable living in a Class context, set the prototype // context of the function to the context of the variable if (rightType.declaration && leftCtx->type() == DUContext::Class) { auto func = rightType.declaration.dynamicCast(); if (!QmlJS::getOwnerOfContext(leftCtx) && !leftCtx->importers().isEmpty()) { // MyClass.prototype.myfunc declares "myfunc" in a small context // that is imported by MyClass. The prototype of myfunc should // be the context of MyClass, not the small context in which // it has been declared leftCtx = leftCtx->importers().at(0); } if (func && !func->prototypeContext()) { func->setPrototypeContext(leftCtx); } } if (leftType.declaration->topContext() != topContext()) { // Do not modify a declaration of another file } else if (leftType.isPrototype && leftInternalCtx) { // Assigning something to a prototype is equivalent to making it // inherit from a class: "Class.prototype = ClassOrObject;" leftInternalCtx->clearImportedParentContexts(); QmlJS::importDeclarationInContext( leftInternalCtx, rightType.declaration ); } else { // Merge the already-known type of the variable with the new one leftType.declaration->setAbstractType(QmlJS::mergeTypes(leftType.type, rightType.type)); } } return false; // findType has already explored node } return DeclarationBuilderBase::visit(node); } bool DeclarationBuilder::visit(QmlJS::AST::CallExpression* node) { inferArgumentsFromCall(node->base, node->arguments); return false; } bool DeclarationBuilder::visit(QmlJS::AST::NewMemberExpression* node) { inferArgumentsFromCall(node->base, node->arguments); return false; } /* * Arrays */ void DeclarationBuilder::declareFieldMember(const KDevelop::DeclarationPointer& declaration, const QString& member, QmlJS::AST::Node* node, const QmlJS::AST::SourceLocation& location) { if (QmlJS::isPrototypeIdentifier(member)) { // Don't declare "prototype", this is a special member return; } if (!m_session->allDependenciesSatisfied()) { // Don't declare anything automatically if dependencies are missing: the // checks hereafter may pass now but fail later, thus causing disappearing // declarations return; } DUChainWriteLocker lock; Identifier identifier(member); // Declaration must have an internal context so that the member can be added // into it. DUContext* ctx = QmlJS::getInternalContext(declaration); if (!ctx || ctx->topContext() != topContext()) { return; } // No need to re-declare a field if it already exists // TODO check if we can make getDeclaration receive an Identifier directly if (QmlJS::getDeclaration(QualifiedIdentifier(identifier), ctx, false)) { return; } // The internal context of declaration is already closed and does not contain // location. This can be worked around by opening a new context, declaring the // new field in it, and then adding the context as a parent of // declaration->internalContext(). RangeInRevision range = m_session->locationToRange(location); IntegralType::Ptr type = IntegralType::Ptr(new IntegralType(IntegralType::TypeMixed)); DUContext* importedContext = openContext(node, range, DUContext::Class); auto* decl = openDeclaration(identifier, range); decl->setInSymbolTable(false); // This declaration is in an anonymous context, and the symbol table acts as if the declaration was in the global context openType(type); closeAndAssignType(); closeContext(); ctx->addImportedParentContext(importedContext); } bool DeclarationBuilder::visit(QmlJS::AST::FieldMemberExpression* node) { setComment(m_session->commentForLocation(node->firstSourceLocation()).toUtf8()); ExpressionType type = findType(node->base); if (type.declaration) { declareFieldMember( type.declaration, node->name.toString(), node, node->identifierToken ); } return false; // findType has already visited node->base } bool DeclarationBuilder::visit(QmlJS::AST::ArrayMemberExpression* node) { setComment(m_session->commentForLocation(node->firstSourceLocation()).toUtf8()); // When the user types array["new_key"], declare "new_key" as a new field of // array. auto stringLiteral = QmlJS::AST::cast(node->expression); if (!stringLiteral) { return DeclarationBuilderBase::visit(node); } ExpressionType type = findType(node->base); if (type.declaration) { declareFieldMember( type.declaration, stringLiteral->value.toString(), node, stringLiteral->literalToken ); } node->expression->accept(this); return false; // findType has already visited node->base, and we have just visited node->expression } bool DeclarationBuilder::visit(QmlJS::AST::ObjectLiteral* node) { setComment(m_session->commentForLocation(node->firstSourceLocation()).toUtf8()); // Object literals can appear in the "values" property of enumerations. Their // keys must be declared in the enumeration, not in an anonymous class if (currentContext()->type() == DUContext::Enum) { return DeclarationBuilderBase::visit(node); } // Open an anonymous class declaration, with its internal context StructureType::Ptr type(new StructureType); { DUChainWriteLocker lock; auto* decl = openDeclaration( Identifier(), QmlJS::emptyRangeOnLine(node->lbraceToken) ); decl->setKind(Declaration::Type); decl->setInternalContext(openContext( node, m_session->locationsToRange(node->lbraceToken, node->rbraceToken), DUContext::Class )); type->setDeclaration(decl); // Every object literal inherits from Object QmlJS::importObjectContext(currentContext(), topContext()); } openType(type); return DeclarationBuilderBase::visit(node); } bool DeclarationBuilder::visit(QmlJS::AST::PropertyNameAndValue* node) { setComment(node); if (!node->name || !node->value) { return DeclarationBuilderBase::visit(node); } RangeInRevision range(m_session->locationToRange(node->name->propertyNameToken)); Identifier name(QmlJS::getNodeValue(node->name)); // The type of the declaration can either be an enumeration value or the type // of its expression ExpressionType type; bool inSymbolTable = false; if (currentContext()->type() == DUContext::Enum) { // This is an enumeration value auto value = QmlJS::AST::cast(node->value); EnumeratorType::Ptr enumerator(new EnumeratorType); enumerator->setDataType(IntegralType::TypeInt); if (value) { enumerator->setValue((int)value->value); } type.type = AbstractType::Ptr::staticCast(enumerator); type.declaration = nullptr; inSymbolTable = true; } else { // Normal value type = findType(node->value); } // If a function is assigned to an object member, set the prototype context // of the function to the object containing the member if (type.declaration) { DUChainWriteLocker lock; auto func = type.declaration.dynamicCast(); if (func && !func->prototypeContext()) { func->setPrototypeContext(currentContext()); } } // Open the declaration { DUChainWriteLocker lock; auto* decl = openDeclaration(name, range); decl->setInSymbolTable(inSymbolTable); } openType(type.type); return false; // findType has already explored node->expression } void DeclarationBuilder::endVisit(QmlJS::AST::PropertyNameAndValue* node) { DeclarationBuilderBase::endVisit(node); closeAndAssignType(); } void DeclarationBuilder::endVisit(QmlJS::AST::ObjectLiteral* node) { DeclarationBuilderBase::endVisit(node); if (currentContext()->type() != DUContext::Enum) { // Enums are special-cased in visit(ObjectLiteral) closeContext(); closeAndAssignType(); } } /* * plugins.qmltypes files */ void DeclarationBuilder::declareComponent(QmlJS::AST::UiObjectInitializer* node, const RangeInRevision &range, const Identifier &name) { QString baseClass = QmlJS::getQMLAttributeValue(node->members, QStringLiteral("prototype")).value.section(QLatin1Char('/'), -1, -1); // Declare the component itself StructureType::Ptr type(new StructureType); ClassDeclaration* decl; { DUChainWriteLocker lock; decl = openDeclaration(name, range); decl->setKind(Declaration::Type); decl->setClassType(ClassDeclarationData::Interface); decl->clearBaseClasses(); if (!baseClass.isEmpty()) { addBaseClass(decl, baseClass); } type->setDeclaration(decl); decl->setType(type); // declareExports needs to know the type of decl } openType(type); } void DeclarationBuilder::declareMethod(QmlJS::AST::UiObjectInitializer* node, const RangeInRevision &range, const Identifier &name, bool isSlot, bool isSignal) { QString type_name = QmlJS::getQMLAttributeValue(node->members, QStringLiteral("type")).value; QmlJS::FunctionType::Ptr type(new QmlJS::FunctionType); if (type_name.isEmpty()) { type->setReturnType(typeFromName(QStringLiteral("void"))); } else { type->setReturnType(typeFromName(type_name)); } { DUChainWriteLocker lock; auto* decl = openDeclaration(name, range); decl->setIsSlot(isSlot); decl->setIsSignal(isSignal); type->setDeclaration(decl); } openType(type); } void DeclarationBuilder::declareProperty(QmlJS::AST::UiObjectInitializer* node, const RangeInRevision &range, const Identifier &name) { AbstractType::Ptr type = typeFromName(QmlJS::getQMLAttributeValue(node->members, QStringLiteral("type")).value); { DUChainWriteLocker lock; auto* decl = openDeclaration(name, range); decl->setAbstractType(type); } openType(type); } void DeclarationBuilder::declareParameter(QmlJS::AST::UiObjectInitializer* node, const RangeInRevision &range, const Identifier &name) { QmlJS::FunctionType::Ptr function = currentType(); AbstractType::Ptr type = typeFromName(QmlJS::getQMLAttributeValue(node->members, QStringLiteral("type")).value); Q_ASSERT(function); function->addArgument(type); { DUChainWriteLocker lock; openDeclaration(name, range); } openType(type); } void DeclarationBuilder::declareEnum(const RangeInRevision &range, const Identifier &name) { EnumerationType::Ptr type(new EnumerationType); { DUChainWriteLocker lock; auto* decl = openDeclaration(name, range); decl->setKind(Declaration::Type); decl->setType(type); // The type needs to be set here because closeContext is called before closeAndAssignType and needs to know the type of decl type->setDataType(IntegralType::TypeEnumeration); type->setDeclaration(decl); } openType(type); } void DeclarationBuilder::declareComponentSubclass(QmlJS::AST::UiObjectInitializer* node, const KDevelop::RangeInRevision& range, const QString& baseclass, QmlJS::AST::UiQualifiedId* qualifiedId) { Identifier name( QmlJS::getQMLAttributeValue(node->members, QStringLiteral("name")).value.section(QLatin1Char('/'), -1, -1) ); DUContext::ContextType contextType = DUContext::Class; if (baseclass == QLatin1String("Component")) { // QML component, equivalent to a QML class declareComponent(node, range, name); } else if (baseclass == QLatin1String("Method") || baseclass == QLatin1String("Signal") || baseclass == QLatin1String("Slot")) { // Method (that can also be a signal or a slot) declareMethod(node, range, name, baseclass == QLatin1String("Slot"), baseclass == QLatin1String("Signal")); contextType = DUContext::Function; } else if (baseclass == QLatin1String("Property")) { // A property declareProperty(node, range, name); } else if (baseclass == QLatin1String("Parameter") && currentType()) { // One parameter of a signal/slot/method declareParameter(node, range, name); } else if (baseclass == QLatin1String("Enum")) { // Enumeration. The "values" key contains a dictionary of name -> number entries. declareEnum(range, name); contextType = DUContext::Enum; name = Identifier(); // Enum contexts should have no name so that their members have the correct scope } else { // Define an anonymous subclass of the baseclass. This subclass will // be instantiated when "id:" is encountered name = Identifier(); // Use ExpressionVisitor to find the declaration of the base class DeclarationPointer baseClass = findType(qualifiedId).declaration; StructureType::Ptr type(new StructureType); { DUChainWriteLocker lock; auto* decl = openDeclaration( currentContext()->type() == DUContext::Global ? Identifier(m_session->moduleName()) : name, QmlJS::emptyRangeOnLine(node->lbraceToken) ); decl->clearBaseClasses(); decl->setKind(Declaration::Type); decl->setType(type); // The class needs to know its type early because it contains definitions that depend on that type type->setDeclaration(decl); if (baseClass) { addBaseClass(decl, baseClass->indexedType()); } } openType(type); } // Open a context of the proper type and identifier openContext( node, m_session->locationsToInnerRange(node->lbraceToken, node->rbraceToken), contextType, QualifiedIdentifier(name) ); DUContext* ctx = currentContext(); Declaration* decl = currentDeclaration(); { // Set the inner context of the current declaration, because nested classes // need to know the inner context of their parents DUChainWriteLocker lock; decl->setInternalContext(ctx); if (contextType == DUContext::Enum) { ctx->setPropagateDeclarations(true); } } // If we have have declared a class, import the context of its base classes registerBaseClasses(); } void DeclarationBuilder::declareComponentInstance(QmlJS::AST::ExpressionStatement* expression) { if (!expression) { return; } auto identifier = QmlJS::AST::cast(expression->expression); if (!identifier) { return; } { DUChainWriteLocker lock; injectContext(topContext()); auto* decl = openDeclaration( Identifier(identifier->name.toString()), m_session->locationToRange(identifier->identifierToken) ); closeInjectedContext(); // Put the declaration in the global scope decl->setKind(Declaration::Instance); decl->setType(currentAbstractType()); } closeDeclaration(); } DeclarationBuilder::ExportLiteralsAndNames DeclarationBuilder::exportedNames(QmlJS::AST::ExpressionStatement* exports) { ExportLiteralsAndNames res; if (!exports) { return res; } auto exportslist = QmlJS::AST::cast(exports->expression); if (!exportslist) { return res; } // Explore all the exported symbols for this component and keep only those // having a version compatible with the one of this module QSet knownNames; for (auto it = exportslist->elements; it && it->expression; it = it->next) { auto stringliteral = QmlJS::AST::cast(it->expression); if (!stringliteral) { continue; } // String literal like "Namespace/Class version". QStringList nameAndVersion = stringliteral->value.toString().section(QLatin1Char('/'), -1, -1).split(QLatin1Char(' ')); QString name = nameAndVersion.at(0); if (!knownNames.contains(name)) { knownNames.insert(name); res.append(qMakePair(stringliteral, name)); } } return res; } void DeclarationBuilder::declareExports(const ExportLiteralsAndNames& exports, ClassDeclaration* classdecl) { DUChainWriteLocker lock; // Create the exported versions of the component for (auto& exp : exports) { QmlJS::AST::StringLiteral* literal = exp.first; QString name = exp.second; StructureType::Ptr type(new StructureType); injectContext(currentContext()->parentContext()); // Don't declare the export in its C++-ish component, but in the scope above auto* decl = openDeclaration( Identifier(name), m_session->locationToRange(literal->literalToken) ); closeInjectedContext(); // The exported version inherits from the C++ component decl->setKind(Declaration::Type); decl->setClassType(ClassDeclarationData::Class); decl->clearBaseClasses(); type->setDeclaration(decl); addBaseClass(decl, classdecl->indexedType()); // Open a context for the exported class, and register its base class in it decl->setInternalContext(openContext( literal, DUContext::Class, QualifiedIdentifier(name) )); registerBaseClasses(); closeContext(); openType(type); closeAndAssignType(); } } /* * UI */ void DeclarationBuilder::importDirectory(const QString& directory, QmlJS::AST::UiImport* node) { DUChainWriteLocker lock; QString currentFilePath = currentContext()->topContext()->url().str(); QFileInfo dir(directory); QFileInfoList entries; if (dir.isDir()) { // Import all the files in the given directory entries = QDir(directory).entryInfoList( QStringList{ (QLatin1String("*.") + currentFilePath.section(QLatin1Char('.'), -1, -1)), QStringLiteral("*.qmltypes"), QStringLiteral("*.so")}, QDir::Files ); } else if (dir.isFile()) { // Import the specific file given in the import statement entries.append(dir); } else if (!m_prebuilding) { m_session->addProblem(node, i18n("Module not found, some types or properties may not be recognized")); return; } // Translate the QFileInfos into QStrings (and replace .so files with // qmlplugindump dumps) lock.unlock(); - QStringList filePaths = QmlJS::Cache::instance().getFileNames(entries); + const QStringList filePaths = QmlJS::Cache::instance().getFileNames(entries); lock.lock(); if (node && !node->importId.isEmpty()) { // Open a namespace that will contain the declarations Identifier identifier(node->importId.toString()); RangeInRevision range = m_session->locationToRange(node->importIdToken); auto* decl = openDeclaration(identifier, range); decl->setKind(Declaration::Namespace); decl->setInternalContext(openContext(node, range, DUContext::Class, QualifiedIdentifier(identifier))); } for (const QString& filePath : filePaths) { if (filePath == currentFilePath) { continue; } ReferencedTopDUContext context = m_session->contextOfFile(filePath); if (context) { currentContext()->addImportedParentContext(context.data()); } } if (node && !node->importId.isEmpty()) { // Close the namespace containing the declarations closeContext(); closeDeclaration(); } } void DeclarationBuilder::importModule(QmlJS::AST::UiImport* node) { QmlJS::AST::UiQualifiedId *part = node->importUri; QString uri; while (part) { if (!uri.isEmpty()) { uri.append(QLatin1Char('.')); } uri.append(part->name.toString()); part = part->next; } // Version of the import QString version = m_session->symbolAt(node->versionToken); // Import the directory containing the module QString modulePath = QmlJS::Cache::instance().modulePath(m_session->url(), uri, version); importDirectory(modulePath, node); } bool DeclarationBuilder::visit(QmlJS::AST::UiImport* node) { if (node->importUri) { importModule(node); } else if (!node->fileName.isEmpty() && node->fileName != QLatin1String(".")) { QUrl currentFileUrl = currentContext()->topContext()->url().toUrl(); QUrl importUrl = QUrl(node->fileName.toString()); importDirectory(currentFileUrl.resolved(importUrl).toLocalFile(), node); } return DeclarationBuilderBase::visit(node); } bool DeclarationBuilder::visit(QmlJS::AST::UiObjectDefinition* node) { setComment(node); // Do not crash if the user has typed an empty object definition if (!node->initializer || !node->initializer->members) { m_skipEndVisit.push(true); return DeclarationBuilderBase::visit(node); } RangeInRevision range(m_session->locationToRange(node->qualifiedTypeNameId->identifierToken)); QString baseclass = node->qualifiedTypeNameId->name.toString(); // "Component" needs special care: a component that appears only in a future // version of this module, or that already appeared in a former version, must // be skipped because it is useless ExportLiteralsAndNames exports; if (baseclass == QLatin1String("Component")) { QmlJS::AST::Statement* statement = QmlJS::getQMLAttribute(node->initializer->members, QStringLiteral("exports")); exports = exportedNames(QmlJS::AST::cast(statement)); if (statement && exports.count() == 0) { // This component has an "exports:" member but no export matched // the version of this module. Skip the component m_skipEndVisit.push(true); return false; } } else if (baseclass == QLatin1String("Module")) { // "Module" is disabled. This allows the declarations of a module // dump to appear in the same namespace as the .qml files in the same // directory. m_skipEndVisit.push(true); return true; } // Declare the component subclass declareComponentSubclass(node->initializer, range, baseclass, node->qualifiedTypeNameId); // If we had a component with exported names, declare these exports if (baseclass == QLatin1String("Component")) { auto* classDecl = currentDeclaration(); if (classDecl) { declareExports(exports, classDecl); } } m_skipEndVisit.push(false); return DeclarationBuilderBase::visit(node); } void DeclarationBuilder::endVisit(QmlJS::AST::UiObjectDefinition* node) { DeclarationBuilderBase::endVisit(node); // Do not crash if the user has typed an empty object definition if (!m_skipEndVisit.pop()) { closeContext(); closeAndAssignType(); } } bool DeclarationBuilder::visit(QmlJS::AST::UiScriptBinding* node) { setComment(node); if (!node->qualifiedId) { return DeclarationBuilderBase::visit(node); } // Special-case some binding names QString bindingName = node->qualifiedId->name.toString(); if (bindingName == QLatin1String("id")) { // Instantiate a QML component: its type is the current type (the anonymous // QML class that surrounds the declaration) declareComponentInstance(QmlJS::AST::cast(node->statement)); } // Use ExpressionVisitor to find the signal/property bound DeclarationPointer bindingDecl = findType(node->qualifiedId).declaration; DUChainPointer signal; // If a Javascript block is used as expression or if the script binding is a // slot, open a subcontext so that variables declared in the binding are kept // local, and the signal parameters can be visible to the slot if (( bindingDecl && (signal = bindingDecl.dynamicCast()) && signal->isSignal() ) || node->statement->kind == QmlJS::AST::Node::Kind_Block) { openContext( node->statement, m_session->locationsToInnerRange( node->statement->firstSourceLocation(), node->statement->lastSourceLocation() ), DUContext::Other ); // If this script binding is a slot, import the parameters of its signal if (signal && signal->isSignal() && signal->internalContext()) { DUChainWriteLocker lock; currentContext()->addIndirectImport(DUContext::Import( signal->internalContext(), nullptr )); } } else { // Check that the type of the value matches the type of the property AbstractType::Ptr expressionType = findType(node->statement).type; DUChainReadLocker lock; if (!m_prebuilding && bindingDecl && !areTypesEqual(bindingDecl->abstractType(), expressionType)) { m_session->addProblem(node->qualifiedId, i18n( "Mismatch between the value type (%1) and the property type (%2)", expressionType->toString(), bindingDecl->abstractType()->toString() ), IProblem::Error); } } return DeclarationBuilderBase::visit(node); } void DeclarationBuilder::endVisit(QmlJS::AST::UiScriptBinding* node) { QmlJS::AST::Visitor::endVisit(node); // If visit(UiScriptBinding) has opened a context, close it if (currentContext()->type() == DUContext::Other) { closeContext(); } } bool DeclarationBuilder::visit(QmlJS::AST::UiObjectBinding* node) { setComment(node); if (!node->qualifiedId || !node->qualifiedTypeNameId || !node->initializer) { return DeclarationBuilderBase::visit(node); } // Declare the component subclass. "Behavior on ... {}" is treated exactly // like "Behavior {}". RangeInRevision range = m_session->locationToRange(node->qualifiedTypeNameId->identifierToken); QString baseclass = node->qualifiedTypeNameId->name.toString(); declareComponentSubclass(node->initializer, range, baseclass, node->qualifiedTypeNameId); return DeclarationBuilderBase::visit(node); } void DeclarationBuilder::endVisit(QmlJS::AST::UiObjectBinding* node) { DeclarationBuilderBase::endVisit(node); if (node->qualifiedId && node->qualifiedTypeNameId && node->initializer) { closeContext(); closeAndAssignType(); } } bool DeclarationBuilder::visit(QmlJS::AST::UiPublicMember* node) { setComment(node); RangeInRevision range = m_session->locationToRange(node->identifierToken); Identifier id(node->name.toString()); QString typeName = node->memberTypeName().toString(); bool res = DeclarationBuilderBase::visit(node); // Build the type of the public member if (node->type == QmlJS::AST::UiPublicMember::Signal) { // Open a function declaration corresponding to this signal declareFunction( node, false, Identifier(node->name.toString()), m_session->locationToRange(node->identifierToken), node->parameters, m_session->locationToRange(node->identifierToken), // The AST does not provide the location of the parens nullptr, m_session->locationToRange(node->identifierToken) // A body range must be provided ); // This declaration is a signal and its return type is void { DUChainWriteLocker lock; currentDeclaration()->setIsSignal(true); currentType()->setReturnType(typeFromName(QStringLiteral("void"))); } } else { AbstractType::Ptr type; if (typeName == QLatin1String("alias")) { // Property aliases take the type of their aliased property type = findType(node->statement).type; res = false; // findType has already explored node->statement } else { type = typeFromName(typeName); if (node->typeModifier == QLatin1String("list")) { // QML list, noted "list" in the source file ArrayType::Ptr array(new ArrayType); array->setElementType(type); type = array.cast(); } } { DUChainWriteLocker lock; Declaration* decl = openDeclaration(id, range); decl->setInSymbolTable(false); } openType(type); } return res; } void DeclarationBuilder::endVisit(QmlJS::AST::UiPublicMember* node) { DeclarationBuilderBase::endVisit(node); closeAndAssignType(); } /* * Utils */ void DeclarationBuilder::setComment(QmlJS::AST::Node* node) { setComment(m_session->commentForLocation(node->firstSourceLocation()).toUtf8()); } void DeclarationBuilder::closeAndAssignType() { closeType(); Declaration* dec = currentDeclaration(); Q_ASSERT(dec); if (auto type = lastType()) { DUChainWriteLocker lock; dec->setType(type); } closeDeclaration(); } AbstractType::Ptr DeclarationBuilder::typeFromName(const QString& name) { auto type = IntegralType::TypeNone; QString realName = name; // Built-in types if (name == QLatin1String("string")) { type = IntegralType::TypeString; } else if (name == QLatin1String("bool")) { type = IntegralType::TypeBoolean; } else if (name == QLatin1String("int")) { type = IntegralType::TypeInt; } else if (name == QLatin1String("float")) { type = IntegralType::TypeFloat; } else if (name == QLatin1String("double") || name == QLatin1String("real")) { type = IntegralType::TypeDouble; } else if (name == QLatin1String("void")) { type = IntegralType::TypeVoid; } else if (name == QLatin1String("var") || name == QLatin1String("variant")) { type = IntegralType::TypeMixed; } else if (m_session->language() == QmlJS::Dialect::Qml) { // In QML files, some Qt type names need to be renamed to the QML equivalent if (name == QLatin1String("QFont")) { realName = QStringLiteral("Font"); } else if (name == QLatin1String("QColor")) { realName = QStringLiteral("color"); } else if (name == QLatin1String("QDateTime")) { realName = QStringLiteral("date"); } else if (name == QLatin1String("QDate")) { realName = QStringLiteral("date"); } else if (name == QLatin1String("QTime")) { realName = QStringLiteral("time"); } else if (name == QLatin1String("QRect") || name == QLatin1String("QRectF")) { realName = QStringLiteral("rect"); } else if (name == QLatin1String("QPoint") || name == QLatin1String("QPointF")) { realName = QStringLiteral("point"); } else if (name == QLatin1String("QSize") || name == QLatin1String("QSizeF")) { realName = QStringLiteral("size"); } else if (name == QLatin1String("QUrl")) { realName = QStringLiteral("url"); } else if (name == QLatin1String("QVector3D")) { realName = QStringLiteral("vector3d"); } else if (name.endsWith(QLatin1String("ScriptString"))) { // Q{Declarative,Qml}ScriptString represents a JS snippet auto func = new QmlJS::FunctionType; func->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid))); return AbstractType::Ptr(func); } } if (type == IntegralType::TypeNone) { // Not a built-in type, but a class return typeFromClassName(realName); } else { return AbstractType::Ptr(new IntegralType(type)); } } AbstractType::Ptr DeclarationBuilder::typeFromClassName(const QString& name) { DeclarationPointer decl = QmlJS::getDeclaration(QualifiedIdentifier(name), currentContext()); if (!decl) { if (name == QLatin1String("QRegExp")) { decl = QmlJS::NodeJS::instance().moduleMember(QStringLiteral("__builtin_ecmascript"), QStringLiteral("RegExp"), currentContext()->url()); } } if (decl) { return decl->abstractType(); } else { DelayedType::Ptr type(new DelayedType); type->setKind(DelayedType::Unresolved); type->setIdentifier(IndexedTypeIdentifier(name)); return type; } } void DeclarationBuilder::addBaseClass(ClassDeclaration* classDecl, const QString& name) { addBaseClass(classDecl, IndexedType(typeFromClassName(name))); } void DeclarationBuilder::addBaseClass(ClassDeclaration* classDecl, const IndexedType& type) { BaseClassInstance baseClass; baseClass.access = Declaration::Public; baseClass.virtualInheritance = false; baseClass.baseClass = type; classDecl->addBaseClass(baseClass); } void DeclarationBuilder::registerBaseClasses() { auto* classdecl = currentDeclaration(); DUContext *ctx = currentContext(); if (classdecl) { DUChainWriteLocker lock; for (uint i=0; ibaseClassesSize(); ++i) { const BaseClassInstance &baseClass = classdecl->baseClasses()[i]; StructureType::Ptr baseType = StructureType::Ptr::dynamicCast(baseClass.baseClass.abstractType()); TopDUContext* topctx = topContext(); if (baseType && baseType->declaration(topctx)) { QmlJS::importDeclarationInContext(ctx, DeclarationPointer(baseType->declaration(topctx))); } } } } static bool enumContainsEnumerator(const AbstractType::Ptr& a, const AbstractType::Ptr& b) { Q_ASSERT(a->whichType() == AbstractType::TypeEnumeration); auto aEnum = EnumerationType::Ptr::staticCast(a); Q_ASSERT(b->whichType() == AbstractType::TypeEnumerator); auto bEnumerator = EnumeratorType::Ptr::staticCast(b); return bEnumerator->qualifiedIdentifier().beginsWith(aEnum->qualifiedIdentifier()); } static bool isNumeric(const IntegralType::Ptr& type) { return type->dataType() == IntegralType::TypeInt || type->dataType() == IntegralType::TypeIntegral || type->dataType() == IntegralType::TypeFloat || type->dataType() == IntegralType::TypeDouble; } bool DeclarationBuilder::areTypesEqual(const AbstractType::Ptr& a, const AbstractType::Ptr& b) { if (!a || !b) { return true; } if (a->whichType() == AbstractType::TypeUnsure || b->whichType() == AbstractType::TypeUnsure) { // Don't try to guess something if one of the types is unsure return true; } const auto bIntegral = IntegralType::Ptr::dynamicCast(b); if (bIntegral && (bIntegral->dataType() == IntegralType::TypeString || bIntegral->dataType() == IntegralType::TypeMixed)) { // In QML/JS, a string can be converted to nearly everything else, similarly ignore mixed types return true; } const auto aIntegral = IntegralType::Ptr::dynamicCast(a); if (aIntegral && (aIntegral->dataType() == IntegralType::TypeString || aIntegral->dataType() == IntegralType::TypeMixed)) { // In QML/JS, nearly everything can be to a string, similarly ignore mixed types return true; } if (aIntegral && bIntegral) { if (isNumeric(aIntegral) && isNumeric(bIntegral)) { // Casts between integral types is possible return true; } } if (a->whichType() == AbstractType::TypeEnumeration && b->whichType() == AbstractType::TypeEnumerator) { return enumContainsEnumerator(a, b); } else if (a->whichType() == AbstractType::TypeEnumerator && b->whichType() == AbstractType::TypeEnumeration) { return enumContainsEnumerator(b, a); } { auto aId = dynamic_cast(a.constData()); auto bId = dynamic_cast(b.constData()); if (aId && bId && aId->qualifiedIdentifier() == bId->qualifiedIdentifier()) return true; } { auto aStruct = StructureType::Ptr::dynamicCast(a); auto bStruct = StructureType::Ptr::dynamicCast(b); if (aStruct && bStruct) { auto top = currentContext()->topContext(); auto aDecl = dynamic_cast(aStruct->declaration(top)); auto bDecl = dynamic_cast(bStruct->declaration(top)); if (aDecl && bDecl) { if (aDecl->isPublicBaseClass(bDecl, top) || bDecl->isPublicBaseClass(aDecl, top)) { return true; } } } } return a->equals(b.constData()); } diff --git a/plugins/qmljs/kdevqmljs.json b/plugins/qmljs/kdevqmljs.json index 01caed97d7..459c110ca9 100644 --- a/plugins/qmljs/kdevqmljs.json +++ b/plugins/qmljs/kdevqmljs.json @@ -1,69 +1,65 @@ { "KPlugin": { "Category": "Language Support", "Description": "QML/JS language support (based on qmljs from QtCreator)", - "Description[ca@valencia]": "Implementació dels llenguatges QML/JS (basats en el «qmljs» del QtCreator)", "Description[ca]": "Implementació dels llenguatges QML/JS (basats en el «qmljs» del QtCreator)", "Description[de]": "Sprachunterstützung für QML/JS auf der Grundlage von qmljs von QtCreator", - "Description[el]": "Υποστήριξη γλώσσας QML/JS (με βάση το qmljs από το QtCreator)", "Description[en_GB]": "QML/JS language support (based on qmljs from QtCreator)", "Description[es]": "Implementación del lenguaje QML/JS (basado en qmljs de QtCreator)", "Description[et]": "QML/JS keele toetus (põhineb QtCreatori qmljs'il)", "Description[fr]": "Prise en charge du langage QML/JS (utilisant qmljs de QtCreator)", "Description[gl]": "Compatibilidade con QML/JS (baseada en qmljs de QtCreator)", "Description[it]": "Supporto al linguaggio QML/JS (basato su qmljs di QtCreator)", - "Description[nl]": "Taalondersteuning voor QML/JS (gebaseerd op qmljs uit QtCreator)", + "Description[nl]": "QML/JS taalondersteuning (gebaseerd op qmljs uit QtCreator)", "Description[pl]": "Obsługa języka QML/JS (oparta na qmljs z QtCreator)", "Description[pt]": "Suporte para a linguagem QML/JS (baseado no 'qmljs' do QtCreator)", "Description[pt_BR]": "Suporte para a linguagem QML/JS (baseado no 'qmljs' do QtCreator)", - "Description[sk]": "Podpora jazyka QML/JS (založená na qmljs z QtCreator-a)", - "Description[sv]": "QML/JS språkstöd (baserat på qmljs från QtCreator)", + "Description[sk]": "Podpora jazyka QML/JS (založené na qmljs od QtCreator)", + "Description[sv]": "Stöd för språket QML/JS (baserat på qmljs från QtCreator)", "Description[uk]": "Підтримка мови QML/JS (на основі qmljs з QtCreator)", "Description[x-test]": "xxQML/JS language support (based on qmljs from QtCreator)xx", "Description[zh_CN]": "QML/JS 语言支持 (基于 QtCreator 的 qmljs)", "Icon": "text-x-qml", "Id": "kdevqmljs", "License": "GPL", "Name": "QML Support", "Name[ca@valencia]": "Implementació del QML", "Name[ca]": "Implementació del QML", "Name[cs]": "Podpora QML", "Name[de]": "Unterstützung für QML", - "Name[el]": "Υποστήριξη QML", "Name[en_GB]": "QML Support", "Name[es]": "Implementación de QML", - "Name[et]": "QML-i toetus", + "Name[et]": "QML toetus", "Name[fi]": "QML-tuki", "Name[fr]": "Prise en charge de QML", "Name[gl]": "Compatibilidade con QML", - "Name[hu]": "QML támogatás", "Name[it]": "Supporto per QML", "Name[nl]": "Ondersteuning voor QML", "Name[pl]": "Obsługa QML", "Name[pt]": "Suporte para o QML", "Name[pt_BR]": "Suporte à QML", "Name[ru]": "Поддержка QML", "Name[sk]": "Podpora QML", "Name[sl]": "Podpora za QML", "Name[sv]": "QML-stöd", "Name[tr]": "QML Desteği", "Name[uk]": "Підтримка QML", "Name[x-test]": "xxQML Supportxx", "Name[zh_CN]": "QML 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "ILanguageSupport" ], "X-KDevelop-Languages": [ "QML/JS", "JavaScript" ], "X-KDevelop-Mode": "NoGUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-qml", "application/javascript" ] } diff --git a/plugins/qmljs/qmljsparsejob.cpp b/plugins/qmljs/qmljsparsejob.cpp index 52eb7b61c1..23c26c3867 100644 --- a/plugins/qmljs/qmljsparsejob.cpp +++ b/plugins/qmljs/qmljsparsejob.cpp @@ -1,209 +1,209 @@ /************************************************************************************* * Copyright (C) 2012 by Aleix Pol * * Copyright (C) 2012 by Milian Wolff * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "qmljsparsejob.h" #include #include #include #include #include #include #include #include #include #include #include #include "duchain/cache.h" #include "duchain/declarationbuilder.h" #include "duchain/parsesession.h" #include "duchain/usebuilder.h" #include "debug.h" #include using namespace KDevelop; /* * This function has been copied from kdev-clang * * Copyright 2013 Olivier de Gaalon and Milian Wolff * Licensed under the GPL v2+ */ ProjectFileItem* findProjectFileItem(const IndexedString& url) { ProjectFileItem* file = nullptr; const auto& projects = ICore::self()->projectController()->projects(); for (auto project: projects) { - auto files = project->filesForPath(url); + const auto files = project->filesForPath(url); if (files.isEmpty()) { continue; } file = files.last(); // A file might be defined in different targets. // Prefer file items defined inside a target with non-empty includes. for (auto f: files) { if (!dynamic_cast(f->parent())) { continue; } file = f; if (!IDefinesAndIncludesManager::manager()->includes(f, IDefinesAndIncludesManager::ProjectSpecific).isEmpty()) { break; } } } return file; } QmlJsParseJob::QmlJsParseJob(const IndexedString& url, ILanguageSupport* languageSupport) : ParseJob(url, languageSupport) { // Tell the cache that this file has custom include directories if (auto file = findProjectFileItem(url)) { QmlJS::Cache::instance().setFileCustomIncludes( url, IDefinesAndIncludesManager::manager()->includes(file, IDefinesAndIncludesManager::Type( IDefinesAndIncludesManager::ProjectSpecific | IDefinesAndIncludesManager::UserDefined)) ); } else { QmlJS::Cache::instance().setFileCustomIncludes( url, IDefinesAndIncludesManager::manager()->includes(url.str(), IDefinesAndIncludesManager::ProjectSpecific) ); } } void QmlJsParseJob::run(ThreadWeaver::JobPointer pointer, ThreadWeaver::Thread* thread) { Q_UNUSED(pointer) Q_UNUSED(thread) UrlParseLock urlLock(document()); if (abortRequested() || !isUpdateRequired(ParseSession::languageString())) { return; } // Don't parse this file if one of its dependencies is not up to date const auto& dependencies = QmlJS::Cache::instance().dependencies(document()); for (auto& dependency : dependencies) { if (!QmlJS::Cache::instance().isUpToDate(dependency)) { QmlJS::Cache::instance().setUpToDate(document(), false); return; } } qCDebug(KDEV_QMLJS) << "parsing" << document().str(); ProblemPointer p = readContents(); if (p) { //TODO: associate problem with topducontext return; } ParseSession session(document(), QString::fromUtf8(contents().contents), priority()); if (abortRequested()) { return; } ReferencedTopDUContext context; { DUChainReadLocker lock; context = DUChainUtils::standardContextForUrl(document().toUrl()); } if (context) { translateDUChainToRevision(context); context->setRange(RangeInRevision(0, 0, INT_MAX, INT_MAX)); } if (session.ast()) { QReadLocker parseLock(languageSupport()->parseLock()); if (abortRequested()) { abortJob(); return; } DeclarationBuilder builder(&session); context = builder.build(document(), session.ast(), context); if (abortRequested()) { abortJob(); return; } if ( context && minimumFeatures() & TopDUContext::AllDeclarationsContextsAndUses ) { UseBuilder useBuilder(&session); useBuilder.buildUses(session.ast()); } } if (abortRequested()) { abortJob(); return; } if (!context) { DUChainWriteLocker lock; ParsingEnvironmentFile *file = new ParsingEnvironmentFile(document()); file->setLanguage(ParseSession::languageString()); context = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file); DUChain::self()->addDocumentChain(context); } setDuChain(context); // If the file has become up to date, reparse its importers bool dependenciesOk = session.allDependenciesSatisfied(); QmlJS::Cache::instance().setUpToDate(document(), dependenciesOk); if (dependenciesOk) { session.reparseImporters(); } { DUChainWriteLocker lock; context->setProblems(session.problems()); context->setFeatures(minimumFeatures()); ParsingEnvironmentFilePointer file = context->parsingEnvironmentFile(); Q_ASSERT(file); file->setModificationRevision(contents().modification); DUChain::self()->updateContextEnvironment( context->topContext(), file.data() ); } highlightDUChain(); DUChain::self()->emitUpdateReady(document(), duChain()); if (session.isParsedCorrectly()) { qCDebug(KDEV_QMLJS) << "===Success===" << document().str(); } else { qCDebug(KDEV_QMLJS) << "===Failed===" << document().str() << session.problems(); } } diff --git a/plugins/qthelp/kdevelop-qthelp.knsrc b/plugins/qthelp/kdevelop-qthelp.knsrc index af415a5f5c..07186792e1 100644 --- a/plugins/qthelp/kdevelop-qthelp.knsrc +++ b/plugins/qthelp/kdevelop-qthelp.knsrc @@ -1,29 +1,28 @@ [KNewStuff3] Name=API Documentation (QtHelp) Name[ca]=Documentació de l'API (QtHelp) Name[ca@valencia]=Documentació de l'API (QtHelp) Name[cs]=Dokumentace API (QtHelp) Name[de]=API-Dokumentation (QtHelp) -Name[el]=API τεκμηρίωση (QtHelp) Name[en_GB]=API Documentation (QtHelp) Name[es]=Documentación de la API (QtHelp) Name[et]=API dokumentatsioon (QtHelp) Name[fr]=Documentation de l'interface de programmation (QtHelp) Name[gl]=Documentación da API (axuda de Qt) Name[it]=Documentazione API (QtHelp) Name[nl]=API-documentatie (QtHelp) Name[pl]=Dokumentacja API (QtHelp) Name[pt]=Documentação da API (QtHelp) Name[pt_BR]=Documentação da API (QtHelp) Name[sk]=Dokumentácia API (QtHelp) Name[sl]=Dokumentacija API (QtHelp) Name[sv]=API-dokumentation (QtHelp) Name[tr]=API Belgelendirmesi (QtYardım) Name[uk]=Документація з програмного інтерфейсу (QtHelp) Name[x-test]=xxAPI Documentation (QtHelp)xx Name[zh_CN]=API 文档 (QtHelp) ProvidersUrl=https://download.kde.org/ocs/providers.xml Categories=QCH Documentation Files TargetDir=kdevelop-qthelp Uncompress=archive diff --git a/plugins/qthelp/kdevqthelp.json b/plugins/qthelp/kdevqthelp.json index 50573d8ed3..fa28907994 100644 --- a/plugins/qthelp/kdevqthelp.json +++ b/plugins/qthelp/kdevqthelp.json @@ -1,93 +1,90 @@ { "KPlugin": { "Authors": [ { "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Documentation", "Description": "This plugin provides QtHelp integration", "Description[ca@valencia]": "Aquest connector proveeix la integració amb QtHelp", "Description[ca]": "Aquest connector proveeix la integració amb QtHelp", - "Description[de]": "Dieses Modul integriert die Qt-Hilfe", - "Description[el]": "Αυτό το πρόσθετο παρέχει ενσωμάτωση του QtHelp", + "Description[de]": "Dieses Modul integriert QtHelp in KDevelop", "Description[en_GB]": "This plugin provides QtHelp integration", "Description[es]": "Este complemento proporciona la integración de QtHelp", - "Description[et]": "Se plugin võimaldab QtHelpi lõimimist", + "Description[et]": "See plugin pakub QtHelpi lõimimist", "Description[fi]": "Tämä liitännäinen tarjoaa QtHelp-integraation", "Description[fr]": "Ce module fournit une intégration de l'aide Qt", "Description[gl]": "Este complemento fornece integración con QtHelp", "Description[it]": "Questa estensione fornisce l'integrazione di QtHelp", "Description[nl]": "Deze plug-in biedt integratie met QtHelp", "Description[pl]": "Wplata pomoc QtHelp", "Description[pt]": "Este 'plugin' oferece a integração com o QtHelp", "Description[pt_BR]": "Este plugin fornece integração com o QtHelp", "Description[sk]": "Tento modul poskytuje integráciu QtHelp", "Description[sl]": "Ta vstavek omogoča podporo za pomoč za Qt", "Description[sv]": "Insticksprogrammet tillhandahåller integrering av QtHelp", "Description[tr]": "Bu ekleneti QtHelp tümleştirmesi sağlar", "Description[uk]": "За допомогою цього додатка здійснюється інтеграція з QtHelp", "Description[x-test]": "xxThis plugin provides QtHelp integrationxx", "Description[zh_CN]": "此插件提供了 QtHelp 整合", "Icon": "qtlogo", "Id": "kdevqthelp", "License": "GPL", "Name": "Qt Documentation", "Name[ca@valencia]": "Documentació de les Qt", "Name[ca]": "Documentació de les Qt", "Name[cs]": "Dokumentace Qt", "Name[de]": "Qt-Dokumentation", - "Name[el]": "Τεκμηρίωση Qt", "Name[en_GB]": "Qt Documentation", "Name[es]": "Documentación de Qt", "Name[et]": "Qt dokumentatsioon", "Name[fi]": "Qt-dokumentaatio", "Name[fr]": "Documentation Qt", "Name[gl]": "Documentación de Qt", "Name[it]": "Documentazione Qt", "Name[nl]": "Qt-documentatie", "Name[pl]": "Dokumentacja Qt", "Name[pt]": "Documentação do Qt", "Name[pt_BR]": "Documentação do Qt", "Name[sk]": "Dokumentácia Qt", "Name[sl]": "Dokumentacija za Qt", "Name[sv]": "Qt-dokumentation", "Name[tr]": "Qt Belgelendirmesi", "Name[uk]": "Документація з Qt", "Name[x-test]": "xxQt Documentationxx", "Name[zh_CN]": "Qt 文档", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IDocumentationProviderProvider" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/quickopen/kdevquickopen.json b/plugins/quickopen/kdevquickopen.json index cdd383a899..d88a3a33bc 100644 --- a/plugins/quickopen/kdevquickopen.json +++ b/plugins/quickopen/kdevquickopen.json @@ -1,106 +1,91 @@ { "KPlugin": { "Authors": [ { "Name": "David Nolden", "Name[ca@valencia]": "David Nolden", "Name[ca]": "David Nolden", "Name[cs]": "David Nolden", "Name[de]": "David Nolden", - "Name[el]": "David Nolden", "Name[en_GB]": "David Nolden", "Name[es]": "David Nolden", "Name[et]": "David Nolden", "Name[fi]": "David Nolden", "Name[fr]": "David Nolden", "Name[gl]": "David Nolden", "Name[it]": "David Nolden", "Name[nl]": "David Nolden", "Name[nn]": "David Nolden", "Name[pl]": "David Nolden", "Name[pt]": "David Nolden", "Name[pt_BR]": "David Nolden", "Name[ru]": "David Nolden", "Name[sk]": "David Nolden", "Name[sl]": "David Nolden", "Name[sv]": "David Nolden", "Name[tr]": "David Nolden", "Name[uk]": "David Nolden", "Name[x-test]": "xxDavid Noldenxx", "Name[zh_CN]": "David Nolden" } ], "Category": "Core", "Description": "This plugin allows quick access to project files and language-items like classes/functions.", - "Description[ar]": "تسمح هذه الملحقة بالوصول السريع إلى ملفّات المشروع وعناصر اللغة كالأصناف والدّوال.", "Description[ca@valencia]": "Aquest connector permet un ràpid accés als fitxers del projecte i a elements del llenguatge com classes/funcions.", "Description[ca]": "Aquest connector permet un ràpid accés als fitxers del projecte i a elements del llenguatge com classes/funcions.", "Description[cs]": "Tento zásuvný modul umožňuje rychlý přístup k souborům projektu a položkám jazyka jako jsou třídy a funkce.", "Description[de]": "Dieses Modul bietet schnellen Zugriff auf Projektdateien und Sprachelemente wie Klassen und Funktionen.", - "Description[el]": "Το πρόσθετο αυτό επιτρέπει τη γρήγορη πρόσβαση σε αρχεία έργου και αντικείμενα γλώσσας όπως κλάσεις/συναρτήσεις.", "Description[en_GB]": "This plugin allows quick access to project files and language-items like classes/functions.", "Description[es]": "Este complemento permite acceder rápidamente a los archivos del proyecto y a elementos del lenguaje, como clases y funciones.", "Description[et]": "See plugin pakub kiiret ligipääsu projekti failidele ja keele elementidele, näiteks klassidele ja funktsioonidele.", "Description[fr]": "Ce module permet un accès rapide aux fichiers du projet et aux éléments de langage comme les classes / fonctions.", "Description[gl]": "Este complemento permite acceder rapidamente a ficheiros de proxecto e elementos da linguaxe como clases e funcións.", "Description[it]": "Questa estensione permette di accedere rapidamente ai file di progetto e agli elementi del linguaggio come le classi/funzioni.", "Description[nl]": "Deze plugin biedt snel toegamg tot projectbestanden en taal-items zoals classes/functies.", "Description[pl]": "Daje szybki dostęp do plików projektu, a także klas i funkcji.", "Description[pt]": "Este 'plugin' permite o acesso rápido aos ficheiros dos projectos e aos itens das linguagens, como as classes ou funções.", "Description[pt_BR]": "Este plugin permite o rápido acesso a arquivos de projeto e itens da linguagem como classes/funções.", "Description[sk]": "Tento plugin umožňuje rýchly prístup k súborom projektu a položiek jazyka ako triedy/funkcie.", "Description[sl]": "Vstavek omogoča hiter dostop do projektnih datotek in predmetov kot so razredi in funkcije.", "Description[sv]": "Insticksprogrammet ger snabb åtkomst av projektfiler och språkobjekt som klasser och funktioner.", "Description[tr]": "Bu eklenti proje dosyalarına ve sınıflar/fonksiyonlar gibi dil öğelerine hızlı erişim sağlar.", "Description[uk]": "За допомогою цього додатка можна пришвидшити доступ до файлів проєктів і елементів мови на зразок класів або функцій.", "Description[x-test]": "xxThis plugin allows quick access to project files and language-items like classes/functions.xx", "Description[zh_CN]": "此插件允许快速访问工程文件和诸如类/函数的语言工程。", - "Description[zh_TW]": "此外掛程式讓您快速存取專案檔案與一些語言的項目,如類別或函式等。", "Icon": "quickopen", "Id": "kdevquickopen", "License": "GPL", "Name": "Quick Open", - "Name[ar]": "فتح سريع", - "Name[bg]": "Бързо отваряне", - "Name[bs]": "Brzo otvori", "Name[ca@valencia]": "Obertura ràpida", "Name[ca]": "Obertura ràpida", "Name[cs]": "Rychle otevřít", - "Name[da]": "Åbn hurtigt", "Name[de]": "Schnellöffner", - "Name[el]": "Γρήγορο άνοιγμα", "Name[en_GB]": "Quick Open", "Name[es]": "Apertura rápida", "Name[et]": "Kiiravamine", "Name[fr]": "Ouverture rapide", - "Name[ga]": "Oscailt Thapa", "Name[gl]": "Apertura rápida", - "Name[hu]": "Gyors megnyitás", "Name[it]": "Apertura veloce", - "Name[kk]": "Тез ашу", "Name[nb]": "Hurtigåpne", - "Name[nds]": "Fixopmaken", "Name[nl]": "Snel openen", "Name[pl]": "Szybkie otwieranie", "Name[pt]": "Abertura Rápida", - "Name[pt_BR]": "Abrir rapidamente", + "Name[pt_BR]": "Abertura rápida", "Name[ru]": "Быстрый переход", "Name[sk]": "Rýchlo otvoriť", "Name[sl]": "Hitro odpiranje", "Name[sv]": "Snabböppna", "Name[tr]": "Hızlı Aç", - "Name[ug]": "تېز ئېچىش", "Name[uk]": "Швидке відкриття", "Name[x-test]": "xxQuick Openxx", "Name[zh_CN]": "快速打开", - "Name[zh_TW]": "快速開啟", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Interfaces": [ "org.kdevelop.IQuickOpen" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/quickopen/quickopenmodel.cpp b/plugins/quickopen/quickopenmodel.cpp index 9020d66d6f..a3e067f3eb 100644 --- a/plugins/quickopen/quickopenmodel.cpp +++ b/plugins/quickopen/quickopenmodel.cpp @@ -1,490 +1,500 @@ /* This file is part of the KDE libraries Copyright (C) 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "quickopenmodel.h" #include "debug.h" #include #include #include #include #include "expandingtree/expandingtree.h" #include "projectfilequickopen.h" #include "duchainitemquickopen.h" #define QUICKOPEN_USE_ITEM_CACHING using namespace KDevelop; QuickOpenModel::QuickOpenModel(QWidget* parent) : ExpandingWidgetModel(parent) , m_treeView(nullptr) , m_expandingWidgetHeightIncrease(0) , m_resetBehindRow(0) { m_resetTimer = new QTimer(this); m_resetTimer->setSingleShot(true); m_resetTimer->setInterval(0); connect(m_resetTimer, &QTimer::timeout, this, &QuickOpenModel::resetTimer); } void QuickOpenModel::setExpandingWidgetHeightIncrease(int pixels) { m_expandingWidgetHeightIncrease = pixels; } QStringList QuickOpenModel::allScopes() const { QStringList scopes; for (const ProviderEntry& provider : m_providers) { for (const QString& scope : provider.scopes) { if (!scopes.contains(scope)) { scopes << scope; } } } return scopes; } QStringList QuickOpenModel::allTypes() const { QSet types; for (const ProviderEntry& provider : m_providers) { types += provider.types; } return types.values(); } void QuickOpenModel::registerProvider(const QStringList& scopes, const QStringList& types, KDevelop::QuickOpenDataProviderBase* provider) { ProviderEntry e; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + e.scopes = QSet(scopes.begin(), scopes.end()); + e.types = QSet(types.begin(), types.end()); +#else e.scopes = QSet::fromList(scopes); e.types = QSet::fromList(types); +#endif e.provider = provider; m_providers << e; //.insert( types, e ); connect(provider, &QuickOpenDataProviderBase::destroyed, this, &QuickOpenModel::destroyed); restart(true); } bool QuickOpenModel::removeProvider(KDevelop::QuickOpenDataProviderBase* provider) { bool ret = false; for (ProviderList::iterator it = m_providers.begin(); it != m_providers.end(); ++it) { if ((*it).provider == provider) { m_providers.erase(it); disconnect(provider, &QuickOpenDataProviderBase::destroyed, this, &QuickOpenModel::destroyed); ret = true; break; } } restart(true); return ret; } void QuickOpenModel::enableProviders(const QStringList& _items, const QStringList& _scopes) { - QSet items = QSet::fromList(_items); - QSet scopes = QSet::fromList(_scopes); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const QSet items(_items.begin(), _items.end()); + const QSet scopes(_scopes.begin(), _scopes.end()); +#else + const QSet items = QSet::fromList(_items); + const QSet scopes = QSet::fromList(_scopes); +#endif if (m_enabledItems == items && m_enabledScopes == scopes && !items.isEmpty() && !scopes.isEmpty()) { return; } m_enabledItems = items; m_enabledScopes = scopes; qCDebug(PLUGIN_QUICKOPEN) << "params " << items << " " << scopes; //We use 2 iterations here: In the first iteration, all providers that implement QuickOpenFileSetInterface are initialized, then the other ones. //The reason is that the second group can refer to the first one. for (auto& provider : m_providers) { if (!qobject_cast(provider.provider)) { continue; } qCDebug(PLUGIN_QUICKOPEN) << "comparing" << provider.scopes << provider.types; if ((scopes.isEmpty() || !(scopes & provider.scopes).isEmpty()) && (!(items & provider.types).isEmpty() || items.isEmpty())) { qCDebug(PLUGIN_QUICKOPEN) << "enabling " << provider.types << " " << provider.scopes; provider.enabled = true; provider.provider->enableData(_items, _scopes); } else { qCDebug(PLUGIN_QUICKOPEN) << "disabling " << provider.types << " " << provider.scopes; provider.enabled = false; if ((scopes.isEmpty() || !(scopes & provider.scopes).isEmpty())) { provider.provider->enableData(_items, _scopes); //The provider may still provide files } } } for (auto & provider : m_providers) { if (qobject_cast(provider.provider)) { continue; } qCDebug(PLUGIN_QUICKOPEN) << "comparing" << provider.scopes << provider.types; if ((scopes.isEmpty() || !(scopes & provider.scopes).isEmpty()) && (!(items & provider.types).isEmpty() || items.isEmpty())) { qCDebug(PLUGIN_QUICKOPEN) << "enabling " << provider.types << " " << provider.scopes; provider.enabled = true; provider.provider->enableData(_items, _scopes); } else { qCDebug(PLUGIN_QUICKOPEN) << "disabling " << provider.types << " " << provider.scopes; provider.enabled = false; } } restart(true); } void QuickOpenModel::textChanged(const QString& str) { if (m_filterText == str) { return; } beginResetModel(); m_filterText = str; for (const ProviderEntry& provider : qAsConst(m_providers)) { if (provider.enabled) { provider.provider->setFilterText(str); } } m_cachedData.clear(); clearExpanding(); //Get the 50 first items, so the data-providers notice changes without ui-glitches due to resetting for (int a = 0; a < 50 && a < rowCount(QModelIndex()); ++a) { getItem(a, true); } endResetModel(); } void QuickOpenModel::restart(bool keepFilterText) { // make sure we do not restart recursively which could lead to // recursive loading of provider plugins e.g. (happened for the cpp plugin) QMetaObject::invokeMethod(this, "restart_internal", Qt::QueuedConnection, Q_ARG(bool, keepFilterText)); } void QuickOpenModel::restart_internal(bool keepFilterText) { if (!keepFilterText) { m_filterText.clear(); } bool anyEnabled = std::any_of(m_providers.constBegin(), m_providers.constEnd(), [](const ProviderEntry& e) { return e.enabled; }); if (!anyEnabled) { return; } for (const ProviderEntry& provider : qAsConst(m_providers)) { if (!qobject_cast(provider.provider)) { continue; } ///Always reset providers that implement QuickOpenFileSetInterface and have some matchign scopes, because they may be needed by other providers. if (m_enabledScopes.isEmpty() || !(m_enabledScopes & provider.scopes).isEmpty()) { provider.provider->reset(); } } for (const ProviderEntry& provider : qAsConst(m_providers)) { if (qobject_cast(provider.provider)) { continue; } if (provider.enabled && provider.provider) { provider.provider->reset(); } } if (keepFilterText) { textChanged(m_filterText); } else { beginResetModel(); m_cachedData.clear(); clearExpanding(); endResetModel(); } } void QuickOpenModel::destroyed(QObject* obj) { removeProvider(static_cast(obj)); } QModelIndex QuickOpenModel::index(int row, int column, const QModelIndex& /*parent*/) const { if (column >= columnCount() || row >= rowCount(QModelIndex())) { return QModelIndex(); } if (row < 0 || column < 0) { return QModelIndex(); } return createIndex(row, column); } QModelIndex QuickOpenModel::parent(const QModelIndex&) const { return QModelIndex(); } int QuickOpenModel::rowCount(const QModelIndex& i) const { if (i.isValid()) { return 0; } int count = 0; for (const ProviderEntry& provider : m_providers) { if (provider.enabled) { count += provider.provider->itemCount(); } } return count; } int QuickOpenModel::unfilteredRowCount() const { int count = 0; for (const ProviderEntry& provider : m_providers) { if (provider.enabled) { count += provider.provider->unfilteredItemCount(); } } return count; } int QuickOpenModel::columnCount() const { return 2; } int QuickOpenModel::columnCount(const QModelIndex& index) const { if (index.parent().isValid()) { return 0; } else { return columnCount(); } } QVariant QuickOpenModel::data(const QModelIndex& index, int role) const { QuickOpenDataPointer d = getItem(index.row()); if (!d) { return QVariant(); } switch (role) { case KTextEditor::CodeCompletionModel::ItemSelected: { QString desc = d->htmlDescription(); if (desc.isEmpty()) { return QVariant(); } else { return desc; } } case KTextEditor::CodeCompletionModel::IsExpandable: return d->isExpandable(); case KTextEditor::CodeCompletionModel::ExpandingWidget: { QVariant v; QWidget* w = d->expandingWidget(); if (w && m_expandingWidgetHeightIncrease) { w->resize(w->width(), w->height() + m_expandingWidgetHeightIncrease); } v.setValue(w); return v; } case ExpandingTree::ProjectPathRole: // TODO: put this into the QuickOpenDataBase API // we cannot do this in 5.0, cannot change ABI if (auto projectFile = dynamic_cast(d.constData())) { return QVariant::fromValue(projectFile->projectPath()); } else if (auto duchainItem = dynamic_cast(d.constData())) { return QVariant::fromValue(duchainItem->projectPath()); } } if (index.column() == 1) { //This column contains the actual content switch (role) { case Qt::DecorationRole: return d->icon(); case Qt::DisplayRole: return d->text(); case KTextEditor::CodeCompletionModel::HighlightingMethod: return KTextEditor::CodeCompletionModel::CustomHighlighting; case KTextEditor::CodeCompletionModel::CustomHighlight: return d->highlighting(); } } else if (index.column() == 0) { //This column only contains the expanded/not expanded icon switch (role) { case Qt::DecorationRole: { if (isExpandable(index)) { //Show the expanded/unexpanded handles if (isExpanded(index)) { return QIcon::fromTheme(QStringLiteral("arrow-down")); } else { return QIcon::fromTheme(QStringLiteral("arrow-right")); } } } } } return ExpandingWidgetModel::data(index, role); } void QuickOpenModel::resetTimer() { int currentRow = treeView() ? mapToSource(treeView()->currentIndex()).row() : -1; beginResetModel(); //Remove all cached data behind row m_resetBehindRow for (DataList::iterator it = m_cachedData.begin(); it != m_cachedData.end(); ) { if (it.key() > m_resetBehindRow) { it = m_cachedData.erase(it); } else { ++it; } } endResetModel(); if (currentRow != -1) { treeView()->setCurrentIndex(mapFromSource(index(currentRow, 0, QModelIndex()))); //Preserve the current index } m_resetBehindRow = 0; } QuickOpenDataPointer QuickOpenModel::getItem(int row, bool noReset) const { ///@todo mix all the models alphabetically here. For now, they are simply ordered. ///@todo Deal with unexpected item-counts, like for example in the case of overloaded function-declarations #ifdef QUICKOPEN_USE_ITEM_CACHING const auto dataIt = m_cachedData.constFind(row); if (dataIt != m_cachedData.constEnd()) { return *dataIt; } #endif int rowOffset = 0; Q_ASSERT(row < rowCount(QModelIndex())); for (const ProviderEntry& provider : m_providers) { if (!provider.enabled) { continue; } uint itemCount = provider.provider->itemCount(); if (( uint )row < itemCount) { QuickOpenDataPointer item = provider.provider->data(row); if (!noReset && provider.provider->itemCount() != itemCount) { qCDebug(PLUGIN_QUICKOPEN) << "item-count in provider has changed, resetting model"; m_resetTimer->start(); m_resetBehindRow = rowOffset + row; //Don't reset everything, only everything behind this position } #ifdef QUICKOPEN_USE_ITEM_CACHING m_cachedData[row + rowOffset] = item; #endif return item; } else { row -= provider.provider->itemCount(); rowOffset += provider.provider->itemCount(); } } // qWarning() << "No item for row " << row; return QuickOpenDataPointer(); } QSet QuickOpenModel::fileSet() const { QSet merged; for (const ProviderEntry& provider : m_providers) { if (m_enabledScopes.isEmpty() || !(m_enabledScopes & provider.scopes).isEmpty()) { if (auto* iface = qobject_cast(provider.provider)) { QSet ifiles = iface->files(); //qCDebug(PLUGIN_QUICKOPEN) << "got file-list with" << ifiles.count() << "entries from data-provider" << typeid(*iface).name(); merged += ifiles; } } } return merged; } QTreeView* QuickOpenModel::treeView() const { return m_treeView; } bool QuickOpenModel::indexIsItem(const QModelIndex& index) const { Q_ASSERT(index.model() == this); Q_UNUSED(index); return true; } void QuickOpenModel::setTreeView(QTreeView* view) { m_treeView = view; } int QuickOpenModel::contextMatchQuality(const QModelIndex& /*index*/) const { return -1; } bool QuickOpenModel::execute(const QModelIndex& index, QString& filterText) { qCDebug(PLUGIN_QUICKOPEN) << "executing model"; if (!index.isValid()) { qCWarning(PLUGIN_QUICKOPEN) << "Invalid index executed"; return false; } QuickOpenDataPointer item = getItem(index.row()); if (item) { return item->execute(filterText); } else { qCWarning(PLUGIN_QUICKOPEN) << "Got no item for row " << index.row() << " "; } return false; } diff --git a/plugins/scratchpad/emptymessagelistview.h b/plugins/scratchpad/emptymessagelistview.h index f41478b2d5..34cafacde1 100644 --- a/plugins/scratchpad/emptymessagelistview.h +++ b/plugins/scratchpad/emptymessagelistview.h @@ -1,40 +1,40 @@ /* This file is part of KDevelop * * Copyright 2018 Amish K. Naidu * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef EMPTYMESSAGELISTVIEW_H #define EMPTYMESSAGELISTVIEW_H #include // subclass to show a message when the list is empty class EmptyMessageListView : public QListView { Q_OBJECT public: - EmptyMessageListView(QWidget* parent); + explicit EmptyMessageListView(QWidget* parent); void setEmptyMessage(const QString& message); protected: void paintEvent(QPaintEvent* event) override; private: QString m_message; }; #endif // EMPTYMESSAGELISTVIEW_H diff --git a/plugins/scratchpad/scratchpad.json b/plugins/scratchpad/scratchpad.json index 9d4c5ecb57..eb8ec5c754 100644 --- a/plugins/scratchpad/scratchpad.json +++ b/plugins/scratchpad/scratchpad.json @@ -1,49 +1,45 @@ { "KPlugin": { "Description": "Scratchpad lets you quickly run and experiment with code without a full project, and even store todos.", - "Description[ca@valencia]": "L'Scratchpad permet executar i experimentar ràpidament amb el codi sense un projecte complet, i inclús emmagatzemar tasques pendents.", "Description[ca]": "L'Scratchpad permet executar i experimentar ràpidament amb el codi sense un projecte complet, i inclús emmagatzemar tasques pendents.", - "Description[el]": "Το scratchpad σας επιτρέπει τη γρήγορη εκτέλεση και πειραματισμό με κώδικα χωρίς πλήρες έργο και αποθηκεύει ακόμη και todos.", "Description[en_GB]": "Scratchpad lets you quickly run and experiment with code without a full project, and even store to-dos.", "Description[es]": "Scratchpad le permite ejecutar y experimentar con código rápidamente con código sin un proyecto completo, e incluso guardar listas de cosas por hacer.", "Description[et]": "Märkmik võimaldab kiiresti käitada koodi ja sellega eksperimenteerida ilma täielikku projekti loomata ning isegi ülesandeid salvestada.", "Description[fr]": "Le brouillon vous permet de lancer et d'essayer rapidement du code sans créer un projet complet, vous pouvez même vous en servir pour stocker des listes de tâches.", "Description[gl]": "O caderno de notas permítelle executar e experimentar con código rapidamente sen un proxecto completo, e mesmo pode gardar listas de tarefas.", "Description[it]": "Il blocco appunti ti permette di sperimentare ed eseguire rapidamente il codice senza un progetto completo, ed anche memorizzare un elenco di cose da fare.", - "Description[nl]": "Kladblol laat u snel code uitvoeren en mee experimenteren zonder een volledig project en zelfs to-do's opslaan.", + "Description[nl]": "Scratchpad lets you quickly run and experiment with code without a full project, and even store todos.", "Description[pl]": "Daje bazgroszyt do szybkiego wykonywania i próbowania kodu poza projektem.", - "Description[pt]": "A Área de Rascunhos permite-lhe executar e experimentar rapidamente código sem ter um projecto completo ou mesmo guardar listas de tarefas.", + "Description[pt]": "O Quadro permite-lhe executar e experimentar rapidamente o código sem ter um projecto completo, ou ainda registar tarefas.", "Description[pt_BR]": "A área de rascunhos permite-lhe executar e experimentar rapidamente o código sem ter um projeto completo ou mesmo guardar listas de tarefas.", "Description[sk]": "Scratchpad vám umožní rýchlo spustiť a experimentovať s kódom bez plného projektu a dokonca ukladať úlohy.", - "Description[sv]": "Kladdblock låter dig snabbt köra och experimentera med kod utan ett fullständigt projekt, och till och med lagra uppgifter.", + "Description[sv]": "Scratchpad låter dig snabbt köra och experimentera med kod utan ett helt projekt, och till och med lagra uppgifter.", "Description[uk]": "За допомогою нотатника ви можете швидко створити фрагмент коду та поекспериментувати з ним без створення повноцінного проєкту. Можливе навіть створення завдань на майбутнє.", "Description[x-test]": "xxScratchpad lets you quickly run and experiment with code without a full project, and even store todos.xx", "Description[zh_CN]": "通过刮板,您可以在没有完整项目的情况下快速运行和试验代码,甚至可以存储待办事项。", "Icon": "note", "Id": "scratchpad", "Name": "Scratchpad", - "Name[ca@valencia]": "Scratchpad", "Name[ca]": "Scratchpad", - "Name[el]": "Scratchpad", "Name[en_GB]": "Scratchpad", "Name[es]": "Scratchpad", "Name[et]": "Märkmik", "Name[fr]": "Brouillon", "Name[gl]": "Caderno de notas", "Name[it]": "Blocco appunti", - "Name[nl]": "Kladblok", + "Name[nl]": "Scratchpad", "Name[pl]": "Bazgroszyt", - "Name[pt]": "Área de Rascunhos", + "Name[pt]": "Quadro", "Name[pt_BR]": "Scratchpad", "Name[sk]": "Scratchpad", - "Name[sv]": "Kladdblock", + "Name[sv]": "Scratchpad", "Name[uk]": "Нотатник для чернеток", "Name[x-test]": "xxScratchpadxx", "Name[zh_CN]": "刮板", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/sourceformatter/kdevsourceformatter.json b/plugins/sourceformatter/kdevsourceformatter.json index e2eba25480..7eba26f92a 100644 --- a/plugins/sourceformatter/kdevsourceformatter.json +++ b/plugins/sourceformatter/kdevsourceformatter.json @@ -1,67 +1,57 @@ { "KPlugin": { "Category": "Utilities", "Description": "Configure which style should be used for formatting of the sourcecode in a project.", "Description[ca@valencia]": "Configura quin estil s'ha d'usar per a formatar el codi font d'un projecte.", "Description[ca]": "Configura quin estil s'ha d'usar per a formatar el codi font d'un projecte.", "Description[de]": "Einstellung des Formatierungsstils für den Quelltext in einem Projekt.", - "Description[el]": "Διαμορφώνει το στιλ για τη μορφή του πηγαίου κώδικα σε ένα έργο.", "Description[en_GB]": "Configure which style should be used for formatting of the sourcecode in a project.", "Description[es]": "Configurar el estilo que se debe usar para formatear el código fuente de un proyecto.", "Description[et]": "Seadistamine, millist stiili kasutada projekti lähtekoodi vormindamisel.", "Description[fr]": "Configurer le style de formatage du code source dans les projets.", "Description[gl]": "Configurar o estilo para usar para formatar o código fonte nun proxecto.", "Description[it]": "Configura lo stile che sarà usato per formattare il codice sorgente in un progetto.", "Description[nl]": "Configureer welke stijl gebruikt moet worden voor formattering van de broncode in een project.", "Description[pl]": "Określa wygląd stosowany do formatowania kodu źródłowego w projekcie.", - "Description[pt]": "Configure o estilo que deverá ser usado na formatação do código-fonte num projecto.", + "Description[pt]": "Configura o estilo que deve ser usado para formatar o código-fonte num projecto.", "Description[pt_BR]": "Configura o estilo que deverá ser usado na formatação do código-fonte em um projeto.", "Description[sk]": "Nastavte, aký štýl sa má použiť na formátovanie zdrojového kódu v projekte.", "Description[sl]": "Nastavite, kateri slog naj bo v projektu uporabljen za oblikovanje izvorne kode.", "Description[sv]": "Anpassa vilken stil som ska användas för att formatera källkoden i ett projekt.", "Description[tr]": "Bir projedeki kaynak kodun biçimlendirilmesi için hangi biçimin kullanılması gerektiğini yapılandırın.", "Description[uk]": "Налаштувати стиль, який буде використано для форматування початкового коду у проєкті.", "Description[x-test]": "xxConfigure which style should be used for formatting of the sourcecode in a project.xx", "Description[zh_CN]": "配置工程中源代码应使用哪个样式进行格式化。", "Icon": "text-field", "Id": "kdevsourceformatter", "Name": "Source Formatter", - "Name[bg]": "Форматиращ модул за програмен текст", "Name[ca@valencia]": "Formatador del codi font", "Name[ca]": "Formatador del codi font", "Name[cs]": "Formátovač zdroje", - "Name[da]": "Formatering af kildekode", "Name[de]": "Quelltextformatierer", - "Name[el]": "Διαμορφωτής πηγαίου κώδικα", "Name[en_GB]": "Source Formatter", "Name[es]": "Formateador de código fuente", - "Name[et]": "Lähtekoodi vormindaja", "Name[fr]": "Formateur de sources", "Name[gl]": "Formatador de código", - "Name[hu]": "Forrásformázó", "Name[it]": "Formattatore sorgenti", - "Name[kk]": "Бастапқы код пішімдеуіші", "Name[nb]": "Kildeformattering", - "Name[nds]": "Bornformateren", "Name[nl]": "Broncode-formatteerprogramma", "Name[pl]": "Formatowanie źródeł", "Name[pt]": "Formatação de Código", "Name[pt_BR]": "Formatador de código", "Name[ru]": "Форматирование кода", "Name[sk]": "Formátovač zdroja", "Name[sl]": "Oblikovalnik izvorne kode", "Name[sv]": "Källkodsformatering", "Name[tr]": "Kaynak Biçimlendirici", - "Name[ug]": "مەنبەنى پىچقۇچ", "Name[uk]": "Форматування коду", "Name[x-test]": "xxSource Formatterxx", "Name[zh_CN]": "源代码格式化器", - "Name[zh_TW]": "源碼格式器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Project", "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/standardoutputview/kdevstandardoutputview.json b/plugins/standardoutputview/kdevstandardoutputview.json index f3f832d8b7..0392666c4e 100644 --- a/plugins/standardoutputview/kdevstandardoutputview.json +++ b/plugins/standardoutputview/kdevstandardoutputview.json @@ -1,72 +1,58 @@ { "KPlugin": { "Category": "Core", "Description": "Provides a text output tool view for other plugins to use, to show things like compiler messages.", "Description[ca@valencia]": "Proporciona una vista d'eina d'eixida de text per a utilitzar en altres connectors, per a visualitzar missatges del compilador, per exemple.", "Description[ca]": "Proporciona una vista d'eina de sortida de text per a utilitzar en altres connectors, per a visualitzar missatges del compilador, per exemple.", "Description[cs]": "Umožňuje používat jiným zásuvným modulům nástrojový pohled textového výstupu, aby ukázal různé věci, jako třeba zprávy kompilátoru.", "Description[de]": "Stellt eine Textausgabe für andere Module zur Verfügung, um Informationen wie Compiler-Nachrichten anzuzeigen.", - "Description[el]": "Παρέχει ένα εργαλείο προβολής κειμένου στην έξοδο για χρήση από άλλα πρόσθετα, για την εμφάνιση μηνυμάτων όπως τα μηνύματα του μεταγλωττιστή.", "Description[en_GB]": "Provides a text output tool view for other plugins to use, to show things like compiler messages.", "Description[es]": "Proporciona un visor de salida de texto para que otros complementos muestren cosas como mensajes del compilador, por ejemplo.", "Description[et]": "Teistele pluginatele kättesaadav tekstiväljundi tööriistavaade, mis näitab kompilaatori teateid ja muud.", "Description[fr]": "Fournit un outil d'affichage de la sortie texte pour utilisation par d'autres modules, pour afficher des choses comme des messages de compilateur.", "Description[gl]": "Fornece unha ferramenta de saída de texto a outros complementos, para mostrar cousas como mensaxes do compilador.", "Description[it]": "Fornisce una vista degli strumenti testuale che può essere usata dalla estensioni per mostrare cose come i messaggi del compilatore.", "Description[nl]": "Levert tekstuitvoer van hulpmiddelen voor andere te gebruiken plugins, om zaken te tonen zoals berichten van compilers.", "Description[pl]": "Udostępnia widok wyniku dla innych wtyczek, aby pokazywały np. dane z kompilatora.", - "Description[pt]": "Oferece uma área de texto para os outros 'plugins' usarem, para apresentar algumas coisas, como as mensagens do compilador.", + "Description[pt]": "Oferece uma área de resultados de texto para os outros 'plugins' usarem, para apresentar algumas coisas, como as mensagens do compilador.", "Description[pt_BR]": "Oferece uma área de resultados de texto para os outros plugins usarem, de modo a mostrar coisas como as mensagens do compilador.", - "Description[sk]": "Poskytuje nástroj textového výstupu na použitie pre iné doplnky, napríklad na zobrazenie správ prekladača.", - "Description[sl]": "Drugim vstavkom ponuja okno za prikaz besedilnega izhoda, na primer za sporočila izgrajevalnika.", + "Description[sk]": "Poskytuje nástroj pohľadu na textový výstup pre iné pluginy na použitie, na zobrazenie informácií ako napríklad správy z prekladača.", "Description[sv]": "Tillhandahåller en verktygsvy för textutmatning som andra insticksprogram kan använda för att visa saker som kompilatormeddelanden.", - "Description[tr]": "Derleyici iletileri gibi şeyleri göstermek amacıyla,diğer eklentilerin kullanması için araç görünümünde bir metin çıkışı sağlar.", "Description[uk]": "Забезпечує роботу панелі показу текстових даних інших додатків, зокрема попереджень компілятора.", "Description[x-test]": "xxProvides a text output tool view for other plugins to use, to show things like compiler messages.xx", "Description[zh_CN]": "提供让其它插件使用的文本输出工具视图,以便显示诸如编译器消息的信息。", "Icon": "kdevelop", "Id": "KDevStandardOutputView", "Name": "Output View", - "Name[ar]": "عرض الخَرْج", - "Name[bg]": "Преглед на резултата", - "Name[bs]": "Pregled izlaza", "Name[ca@valencia]": "Vista de l'eixida", "Name[ca]": "Vista de la sortida", "Name[cs]": "Pohled na výstup", - "Name[da]": "Visning af output", "Name[de]": "Ansicht für Ausgaben", - "Name[el]": "Προβολή αποτελεσμάτων", "Name[en_GB]": "Output View", "Name[es]": "Vista de la salida", "Name[et]": "Väljundivaade", "Name[fr]": "Vue de la sortie", - "Name[ga]": "Amharc Aschurtha", "Name[gl]": "Vista da saída", - "Name[hu]": "Kimenet nézet", "Name[it]": "Vista output", - "Name[kk]": "Шығыс көрінісі", "Name[nb]": "Utdata-visning", - "Name[nds]": "Utgaavansicht", "Name[nl]": "Uitvoerweergave", "Name[pl]": "Widok wyjścia", "Name[pt]": "Área de Resultados", "Name[pt_BR]": "Área de resultados", "Name[ru]": "Панель вывода", "Name[sk]": "Pohľad na výstup", "Name[sl]": "Prikaz izhoda", "Name[sv]": "Utmatningsvisning", "Name[tr]": "Çıktı Görünümü", - "Name[ug]": "چىقىرىش كۆرۈنۈشى", "Name[uk]": "Перегляд виводу", "Name[x-test]": "xxOutput Viewxx", "Name[zh_CN]": "输出视图", - "Name[zh_TW]": "輸出檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/subversion/kdevsubversion.json b/plugins/subversion/kdevsubversion.json index 05789fb3b7..0fd9545c5a 100644 --- a/plugins/subversion/kdevsubversion.json +++ b/plugins/subversion/kdevsubversion.json @@ -1,102 +1,92 @@ { "KPlugin": { "Authors": [ { "Name": "Dukju Ahn", "Name[ca@valencia]": "Dukju Ahn", "Name[ca]": "Dukju Ahn", "Name[cs]": "Dukju Ahn", "Name[de]": "Dukju Ahn", - "Name[el]": "Dukju Ahn", "Name[en_GB]": "Dukju Ahn", "Name[es]": "Dukju Ahn", "Name[et]": "Dukju Ahn", "Name[fr]": "Dukju Ahn", "Name[gl]": "Dukju Ahn", "Name[it]": "Dukju Ahn", "Name[nl]": "Dukju Ahn", "Name[nn]": "Dukju Ahn", "Name[pl]": "Dukju Ahn", "Name[pt]": "Dukju Ahn", "Name[pt_BR]": "Dukju Ahn", "Name[ru]": "Dukju Ahn", "Name[sk]": "Dukju Ahn", "Name[sl]": "Dukju Ahn", "Name[sv]": "Dukju Ahn", "Name[tr]": "Dukju Ahn", "Name[uk]": "Dukju Ahn", "Name[x-test]": "xxDukju Ahnxx", "Name[zh_CN]": "Dukju Ahn" } ], "Category": "Version Control", "Description": "This plugin integrates Subversion to KDevelop.", "Description[ca@valencia]": "Aquest connector integra el Subversion en el KDevelop.", "Description[ca]": "Aquest connector integra el Subversion en el KDevelop.", "Description[cs]": "Tento modul integruje podporu pro subversion v KDevelop", "Description[de]": "Dieses Modul integriert Subversion in KDevelop.", - "Description[el]": "Αυτό το πρόσθετο ενσωματώνει το Subversion στο KDevelop.", "Description[en_GB]": "This plugin integrates Subversion to KDevelop.", "Description[es]": "Este complemento integra Subversion en KDevelop.", - "Description[et]": "See plugin lõimib Subversioni KDevelopiga.", "Description[fr]": "Ce module intègre Subversion dans KDevelop.", "Description[gl]": "Este complemento integra Subversion en KDevelop.", "Description[it]": "Questa estensione integra Subversion in KDevelop.", "Description[nl]": "Deze plugin integreert Subversion in KDevelop.", "Description[pl]": "Wplata Subversion w KDevelop", "Description[pt]": "Este 'plugin' integra o Subversion no KDevelop.", "Description[pt_BR]": "Este plugin integra o Subversion ao KDevelop.", "Description[sk]": "Tento plugin integruje subversion do KDevelop.", "Description[sl]": "Ta vstavek v KDevelop vgradi Subversion", "Description[sv]": "Insticksprogrammet integrerar Subversion i KDevelop.", "Description[tr]": "Bu eklenti Subversion ile KDevelop uygulamasını bütünleştirir.", "Description[uk]": "Цей додаток інтегрує Subversion із KDevelop.", "Description[x-test]": "xxThis plugin integrates Subversion to KDevelop.xx", "Description[zh_CN]": "此插件将 Subversion 整合到 KDevelop。", "Icon": "subversion", "Id": "kdevsubversion", "License": "GPL", "Name": "Subversion Support", - "Name[bg]": "Поддръжка на Subversion", "Name[ca@valencia]": "Implementació del Subversion", "Name[ca]": "Implementació del Subversion", "Name[cs]": "Podpora subversion", - "Name[da]": "Subversion-understøttelse", - "Name[de]": "Unterstützung für Subversion", - "Name[el]": "Υποστήριξη Subversion", + "Name[de]": "Subversion-Unterstützung", "Name[en_GB]": "Subversion Support", "Name[es]": "Implementación de Subversion", "Name[et]": "Subversioni toetus", "Name[fr]": "Prise en charge de Subversion", "Name[gl]": "Soporte de Subversion", "Name[it]": "Supporto per Subversion", - "Name[kk]": "Subversion қолдауы", "Name[nb]": "Støtte for subversion", - "Name[nds]": "Subversion-Ünnerstütten", "Name[nl]": "Ondersteuning van subversion", "Name[nn]": "Subversion-støtte", - "Name[pa]": "ਸਬਵਰਜਨ ਸਹਿਯੋਗ", "Name[pl]": "Obsługa Subversion", "Name[pt]": "Suporte para o Subversion", - "Name[pt_BR]": "Suporte ao Subversion", + "Name[pt_BR]": "Suporte a Subversion", + "Name[ru]": "Поддержка Subversion", "Name[sk]": "Podpora subversion", "Name[sl]": "Podpora za Subversion", "Name[sv]": "Subversion-stöd", "Name[tr]": "Suversion Desteği", - "Name[ug]": "Subversion قوللىشى", "Name[uk]": "Підтримка Subversion", "Name[x-test]": "xxSubversion Supportxx", "Name[zh_CN]": "Subversion 支持", - "Name[zh_TW]": "Subversion 支援", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl" ], "X-KDevelop-Mode": "GUI" } diff --git a/plugins/subversion/org.kde.kdevelop_svn.desktop b/plugins/subversion/org.kde.kdevelop_svn.desktop index 0a458b94d3..92926833cd 100644 --- a/plugins/subversion/org.kde.kdevelop_svn.desktop +++ b/plugins/subversion/org.kde.kdevelop_svn.desktop @@ -1,69 +1,67 @@ [Desktop Entry] Type=Application Exec=kdevelop --ps --fetch %U MimeType=x-scheme-handler/svn;x-scheme-handler/svn+ssh; Icon=kdevelop Terminal=false Name=KDevelop (Fetch Subversion Project) Name[ca]=KDevelop (obtenir un projecte de Subversion) Name[ca@valencia]=KDevelop (obtindre un projecte de Subversion) Name[de]=KDevelop (Subversion-Projekt holen) -Name[el]=KDevelop (Λήψη έργου από Subversion) Name[en_GB]=KDevelop (Fetch Subversion Project) Name[es]=KDevelop (obtener proyecto de Subversion) Name[et]=KDevelop (Subversioni projekti tõmbamine) Name[fr]=KDevelop (récupérer un projet Subversion) Name[gl]=KDevelop (obter un proxecto de Subversion) Name[it]=KDevelop (importa un progetto Subversion) Name[nl]=KDevelop (Subversion-project ophalen) Name[pl]=KDevelop (Pobierz projekt Subversion) Name[pt]=KDevelop (Obter um Projecto do Subversion) Name[pt_BR]=KDevelop (Obter projeto do Subversion) -Name[sk]=KDevelop (Načítať Subversion Projekt) -Name[sl]=KDevelop (pridobi projekt Subversion) +Name[sk]=KDevelop (Stiahnuť Subversion Projekt) Name[sv]=KDevelop (hämta Subversion-projekt) Name[uk]=KDevelop (отримати проєкт Subversion) Name[x-test]=xxKDevelop (Fetch Subversion Project)xx Name[zh_CN]=KDevelop (获取 Subversion 项目) GenericName=Integrated Development Environment GenericName[ar]=بيئة تطوير متكاملة GenericName[bs]=Integrisano razvojno okruženje GenericName[ca]=Entorn integrat de desenvolupament GenericName[ca@valencia]=Entorn integrat de desenvolupament GenericName[cs]=Integrované Vývojové Prostředí GenericName[da]=Integreret udviklingsmiljø (IDE) GenericName[de]=Integrierte Entwicklungsumgebung GenericName[el]=ολοκληρωμένο περιβάλλον ανάπτυξης GenericName[en_GB]=Integrated Development Environment GenericName[es]=Entorno de desarrollo integrado GenericName[et]=Integreeritud arenduskeskkond GenericName[fi]=Integroitu kehitysympäristö GenericName[fr]=Environnement de Développement Intégré GenericName[ga]=Timpeallacht Chomhtháite Fhorbartha GenericName[gl]=Entorno de desenvolvemento integrado GenericName[hne]=एकीकृत डेवलपमेंट वातावरन GenericName[hu]=Integrált fejlesztői környezet GenericName[it]=Ambiente di sviluppo integrato GenericName[ja]=統合開発環境 GenericName[kk]=Біріктірілген құрастыру ортасы GenericName[km]=Development Environment ដែល​បាន​រួមបញ្ចូល GenericName[lt]=Integruota programavimo aplinka GenericName[lv]=Integrēta izstrādes vide GenericName[nb]=Integrert utviklingsmiljø GenericName[nds]=Programmsmeed GenericName[nl]=Geïntegreerde ontwikkelomgeving GenericName[pl]=Zintegrowane środowisko programistyczne GenericName[pt]=Ambiente de Desenvolvimento Integrado GenericName[pt_BR]=Ambiente Integrado de Desenvolvimento GenericName[ru]=Интегрированная среда разработки GenericName[sk]=Integrované vývojové prostredie GenericName[sl]=Integrirano razvojno okolje GenericName[sv]=Integrerad utvecklingsmiljö GenericName[tr]=Bütünleşik Geliştirme Ortamı GenericName[ug]=يۈرۈشلەشتۈرۈلگەن ئىجادىيەت مۇھىتى GenericName[uk]=Комплексне середовище розробки GenericName[x-test]=xxIntegrated Development Environmentxx GenericName[zh_CN]=集成开发环境 GenericName[zh_TW]=整合開發環境 Categories=Qt;KDE;Development;IDE; NoDisplay=true diff --git a/plugins/switchtobuddy/kdevswitchtobuddy.json b/plugins/switchtobuddy/kdevswitchtobuddy.json index c342edb042..f8e74353dd 100644 --- a/plugins/switchtobuddy/kdevswitchtobuddy.json +++ b/plugins/switchtobuddy/kdevswitchtobuddy.json @@ -1,64 +1,56 @@ { "KPlugin": { "Category": "Utilities", "Description": "Allows switching between buddy documents like implementation and header file.", "Description[ca@valencia]": "Permet commutar entre documents associats com fitxers d'implementacions i de capçaleres.", "Description[ca]": "Permet commutar entre documents associats com fitxers d'implementacions i de capçaleres.", "Description[de]": "Ermöglicht das Umschalten zwischen verwandten Dokumenten wie Implementations- und Header-Dateien.", - "Description[el]": "Επιτρέπει την εναλλαγή ανάμεσα σε συμπληρωματικά έγγραφα όπως αρχεία implementation και header", "Description[en_GB]": "Allows switching between buddy documents like implementation and header file.", "Description[es]": "Permite cambiar entre documentos asociados, como la implementación y el archivo de cabecera.", "Description[et]": "Võimaldab lülituda sõltlasdokumentide, näiteks teostus- ja päisefaili vahel.", "Description[fr]": "Permet de basculer entre des documents amis comme l'implémentation et le fichier d'en-tête.", "Description[gl]": "Permite trocar entre documentos tipo buddy como implementación e ficheiro de cabeceira.", "Description[it]": "Consente il passaggio tra i documenti associati come implementazioni e file header.", "Description[nl]": "Biedt omschakelen tussen buddy-documenten zoals implementatie en header-bestand.", "Description[pl]": "Przełącza pomiędzy stowarzyszonymi dokumentami takimi jak pliki implementacji i nagłówków.", "Description[pt]": "Permite a alternância entre os documentos associados, como o ficheiro de implementação ou o de inclusão.", "Description[pt_BR]": "Permite a mudança de documentos associados, como os arquivos de implementação e de inclusão.", "Description[sk]": "Umožňuje prepínanie medzi dokumentmi priateľov ako súbor implementácie a hlavičky.", "Description[sl]": "Omogoča preklapljanje med prijateljskimi dokumenti kot na primer izvedbo in datoteko glave.", "Description[sv]": "Tillåter byte mellan samhörande dokument som implementerings- och deklarationsfiler.", "Description[tr]": "Uygulama ve başlık dosyası gibi ilişkili belgeler arasında geçişe izin verir.", "Description[uk]": "Надає змогу перемикатися на споріднені документи, зокрема файли з реалізацією та файли заголовків.", "Description[x-test]": "xxAllows switching between buddy documents like implementation and header file.xx", "Description[zh_CN]": "允许在伙伴文件中切换,如实现与头文件", - "Description[zh_TW]": "允許在相關文件,如實作與標頭檔間切換。", "Icon": "document-multiple", "Id": "kdevswitchtobuddy", "Name": "Switch to Buddy", - "Name[bs]": "Pređi na prijatelja", "Name[ca@valencia]": "Commuta a associat", "Name[ca]": "Commuta a associat", - "Name[da]": "Skift til vennefil", "Name[de]": "Zu verwandtem Element wechseln", - "Name[el]": "Εναλλαγή στο συμπληρωματικό", "Name[en_GB]": "Switch to Buddy", "Name[es]": "Cambiar al asociado", "Name[et]": "Lülitumine sõltlasele", "Name[fr]": "Passage à Buddy", "Name[gl]": "Pasar a Buddy", - "Name[hu]": "Váltás az ismerősre", "Name[it]": "Passa al file associato", - "Name[kk]": "Тиістіге ауысу", "Name[nb]": "Bytt til kamerat", "Name[nl]": "Naar Buddy omschakelen", "Name[pl]": "Przejdź do stowarzyszonego", "Name[pt]": "Mudar para o Ficheiro Associado", "Name[pt_BR]": "Mudar para o arquivo associado", "Name[ru]": "Переход к связанному", "Name[sk]": "Prepnúť na Buddy", "Name[sl]": "Preklopi na prijatelja", "Name[sv]": "Byt till samhörande", "Name[tr]": "Dosta Geçiş Yap", "Name[uk]": "Перемикання на споріднений", "Name[x-test]": "xxSwitch to Buddyxx", "Name[zh_CN]": "切换至伙伴文件", - "Name[zh_TW]": "切換到相關文件", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/testview/kdevtestview.json b/plugins/testview/kdevtestview.json index f002628106..b11618521a 100644 --- a/plugins/testview/kdevtestview.json +++ b/plugins/testview/kdevtestview.json @@ -1,96 +1,87 @@ { "KPlugin": { "Authors": [ { "Name": "Miha Čančula", "Name[ca@valencia]": "Miha Čančula", "Name[ca]": "Miha Čančula", "Name[cs]": "Miha Čančula", "Name[de]": "Miha Čančula", - "Name[el]": "Miha Čančula", "Name[en_GB]": "Miha Čančula", "Name[es]": "Miha Čančula", "Name[et]": "Miha Čančula", "Name[fr]": "Miha Čančula", "Name[gl]": "Miha Čančula", "Name[it]": "Miha Čančula", "Name[nl]": "Miha Čančula", "Name[nn]": "Miha Čančula", "Name[pl]": "Miha Čančula", "Name[pt]": "Miha Čančula", "Name[pt_BR]": "Miha Čančula", "Name[ru]": "Miha Čančula", "Name[sk]": "Miha Čančula", "Name[sl]": "Miha Čančula", "Name[sv]": "Miha Čančula", "Name[tr]": "Miha Čančula", "Name[uk]": "Miha Čančula", "Name[x-test]": "xxMiha Čančulaxx", "Name[zh_CN]": "Miha Čančula" } ], "Category": "Testing", "Description": "Lets you see and run unit tests.", "Description[ca@valencia]": "Vos permet veure i executar proves unitàries.", "Description[ca]": "Us permet veure i executar proves unitàries.", "Description[cs]": "Umožňuje vám zobrazit a spouštět unit testy.", "Description[de]": "Unit-Tests anzeigen und ausführen.", - "Description[el]": "Σας επιτρέπει να δείτε και να εκτελείτε unit tests.", "Description[en_GB]": "Lets you see and run unit tests.", "Description[es]": "Le permite ver y ejecutar pruebas unitarias.", "Description[et]": "Ühiktestide näitamine ja käivitamine.", "Description[fr]": "Vous permet de voir et exécuter des tests unitaires.", "Description[gl]": "Permite ver e executar probas unitarias.", "Description[it]": "Consente di vedere ed eseguire i test d'unità.", "Description[nl]": "Laat u testen van eenheden zien en uitvoeren.", "Description[pl]": "Umożliwia uruchomienie i obejrzenie testów jednostkowych.", "Description[pt]": "Permite-lhe ver e executar testes unitários.", "Description[pt_BR]": "Permite-lhe ver e executar testes unitários.", "Description[sk]": "Umožní vám vidieť a spustiť unit testy.", "Description[sl]": "Omogoča ogled in zagon preizkusov enot.", "Description[sv]": "Låter dig titta på och köra enhetstester.", "Description[tr]": "Birim testlerini görmenizi ve çalıştırmanızı sağlar.", "Description[uk]": "Надає вам змогу переглядати і виконувати перевірки модулів.", "Description[x-test]": "xxLets you see and run unit tests.xx", "Description[zh_CN]": "让您查看并运行单元测试。", - "Description[zh_TW]": "讓您查看並執行單元測試。", "Icon": "preflight-verifier", "Id": "kdevtestview", "License": "GPL", "Name": "Unit Test View", - "Name[bs]": "Pogled testa jedinica", "Name[ca@valencia]": "Vista de proves unitàries", "Name[ca]": "Vista de proves unitàries", "Name[cs]": "Pohled na Unit testy", - "Name[da]": "Visning af unittests", "Name[de]": "Unittest-Ansicht", - "Name[el]": "Προβολή unit test", "Name[en_GB]": "Unit Test View", "Name[es]": "Vista de la prueba unitaria", "Name[et]": "Ühiktestide vaade", "Name[fr]": "Affichage des tests unitaires", "Name[gl]": "Vista de probas unitarias", - "Name[hu]": "Egységteszt nézet", "Name[it]": "Vista test d'unità", - "Name[kk]": "Модульді сынау көрінісі", "Name[nb]": "Enhetstest-visning", "Name[nl]": "Weergave van test van eenheid", "Name[pl]": "Widok jednostkowego testu", "Name[pt]": "Área de Testes Unitários", "Name[pt_BR]": "Exibição de testes unitários", "Name[ru]": "Панель модульных тестов", "Name[sk]": "Pohľad unit testov", "Name[sl]": "Prikaz preizkusov enot", "Name[sv]": "Visning av enhetstester", "Name[tr]": "Birim Test Görünümü", "Name[uk]": "Перегляд перевірок модулів", "Name[x-test]": "xxUnit Test Viewxx", "Name[zh_CN]": "单元测试视图", - "Name[zh_TW]": "單元測試檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/vcschangesview/kdevvcschangesview.json b/plugins/vcschangesview/kdevvcschangesview.json index 9a13c95956..551ee2bfa9 100644 --- a/plugins/vcschangesview/kdevvcschangesview.json +++ b/plugins/vcschangesview/kdevvcschangesview.json @@ -1,102 +1,87 @@ { "KPlugin": { "Authors": [ { "Email": "aleixpol@kde.org", "Name": "Aleix Pol", "Name[ca@valencia]": "Aleix Pol", "Name[ca]": "Aleix Pol", "Name[cs]": "Aleix Pol", "Name[de]": "Aleix Pol", - "Name[el]": "Aleix Pol", "Name[en_GB]": "Aleix Pol", "Name[es]": "Aleix Pol", "Name[et]": "Aleix Pol", "Name[fi]": "Aleix Pol", "Name[fr]": "Aleix Pol", "Name[gl]": "Aleix Pol", "Name[it]": "Aleix Pol", "Name[nl]": "Aleix Pol", "Name[nn]": "Aleix Pol", "Name[pl]": "Aleix Pol", "Name[pt]": "Aleix Pol", "Name[pt_BR]": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[sk]": "Aleix Pol", "Name[sl]": "Aleix Pol", "Name[sv]": "Aleix Pol", "Name[tr]": "Aleix Pol", "Name[uk]": "Aleix Pol", "Name[x-test]": "xxAleix Polxx", "Name[zh_CN]": "Aleix Pol" } ], "Category": "Version Control", "Description": "This plugin provides integration between the projects and their VCS infrastructure", - "Description[ar]": "توفّر هذه الملحقة التّكامل بين مشاريعك ومركزيّة VCS", "Description[ca@valencia]": "Aquest connector proporciona la integració entre els projectes i la seua infraestructura de VCS", "Description[ca]": "Aquest connector proporciona la integració entre els projectes i la seva infraestructura de VCS", "Description[de]": "Dieses Modul stellt eine Integration zwischen Projekten und ihrer VCS-Infrastruktur her.", - "Description[el]": "Το πρόσθετο αυτό παρέχει ολοκλήρωση ανάμεσα στα έργα και της VCS υποδομής τους", "Description[en_GB]": "This plugin provides integration between the projects and their VCS infrastructure", "Description[es]": "Este complemento proporciona integración entre los proyectos y su infraestructura VCS", "Description[et]": "See plugin võimaldab lõimida projektid ja nende versioonihalduse infrastruktuuri", "Description[fr]": "Ce module fournit une intégration entre les projets et leur infrastructure de contrôle de version", "Description[gl]": "Este complemento fornece integración entre os proxectos e a súa infraestrutura VCS", "Description[it]": "Questa estensione fornisce integrazione tra i progetti e la loro infrastruttura VCS", "Description[nl]": "Deze plugin geeft integratie tussen de projecten en hun VCS-infrastructuur", "Description[pl]": "Wplata obsługę zarządzania wersjami do projektów", "Description[pt]": "Este 'plugin' oferece a integração entre os projectos e a sua infra-estrutura de controlo de versões", "Description[pt_BR]": "Este plugin fornece a integração entre os projetos e sua infraestrutura de VCS", "Description[sk]": "Tento plugin poskytuje integráciu medzi projektami a ich VCS infraštruktúrou", "Description[sl]": "Ta vstavek ponuja most med projekti in njihovo infrastrukturo nadzora različic", "Description[sv]": "Det här insticksprogrammet tillhandahåller integrering mellan projekten och deras VCS infrastruktur", "Description[tr]": "Bu eklenti projeler ile VCS altyapıları arasında bütünleşme sağlar", "Description[uk]": "За допомогою цього додатка забезпечується інтеграція між проєктами та інфраструктурою системи керування версіями (VCS)", "Description[x-test]": "xxThis plugin provides integration between the projects and their VCS infrastructurexx", "Description[zh_CN]": "此插件提供工程和它们的代码管理系统的集成", - "Description[zh_TW]": "此外掛程式提供專案與它們之間版本控制系統基礎間的整合", "Icon": "kdevelop", "Id": "kdevvcschangesviewplugin", "License": "GPL", "Name": "VCS Integration", - "Name[ar]": "تكامل VCS", - "Name[bg]": "Интеграция с VCS", - "Name[bs]": "VCS Integracija", "Name[ca@valencia]": "Integració de VCS", "Name[ca]": "Integració de VCS", "Name[cs]": "Integrace VCS", - "Name[da]": "VCS-integration", "Name[de]": "VCS-Integration", - "Name[el]": "VCS ολοκλήρωση", "Name[en_GB]": "VCS Integration", "Name[es]": "Integración de VCS", - "Name[et]": "Versioonihalduse lõimimine", "Name[fr]": "Intégration de VCS", "Name[gl]": "Integración con VCS", - "Name[hu]": "VCS integráció", "Name[it]": "Integrazione VCS", - "Name[kk]": "Нұсқаларын қадағалау жүйесімен біріктіру ", "Name[nb]": "VCS-integrering", - "Name[nds]": "VKS-Inbinnen", "Name[nl]": "VCS-integratie", "Name[pl]": "Wplecenie systemu zarządzania wersjami", "Name[pt]": "Integração com o SCV", "Name[pt_BR]": "Integração com o VCS", "Name[ru]": "Интеграция VCS", "Name[sk]": "Integrácia VCS", "Name[sl]": "Vgradnja VCS", "Name[sv]": "Integrering av VCS", "Name[tr]": "Sürüm Kontrol Sistemi Bütünleşmesi", - "Name[ug]": "VCS يۈرۈشلەشتۈرۈلۈشى", "Name[uk]": "Інтеграція з системами керування версіями", "Name[x-test]": "xxVCS Integrationxx", "Name[zh_CN]": "VCS 工程集成", - "Name[zh_TW]": "版本控制系統整合", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/welcomepage/declarative/kdevelopdashboarddeclarativeplugin.cpp b/plugins/welcomepage/declarative/kdevelopdashboarddeclarativeplugin.cpp index e3ed07d521..e7436e43a0 100644 --- a/plugins/welcomepage/declarative/kdevelopdashboarddeclarativeplugin.cpp +++ b/plugins/welcomepage/declarative/kdevelopdashboarddeclarativeplugin.cpp @@ -1,62 +1,66 @@ /************************************************************************************* * Copyright (C) 2012 by Aleix Pol * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "kdevelopdashboarddeclarativeplugin.h" #include "debug.h" #include "icoreobject.h" #include #include #include #include class Logger : public QObject { Q_OBJECT public: explicit Logger(const QLoggingCategory &category, QObject *parent = nullptr) : QObject(parent) , m_category(category) {} Q_INVOKABLE void log(const QString &message) { qCDebug(m_category) << message; } private: const QLoggingCategory &m_category; }; void KDevplatformDeclarativePlugin::registerTypes(const char* uri) { static const QLoggingCategory loggingCategory{"kdevelop.plugins.welcomepage"}; qmlRegisterSingletonType(uri, 1, 0, "ICore", [](QQmlEngine*, QJSEngine*) -> QObject* { return KDevelop::ICore::self(); }); qmlRegisterSingletonType(uri, 1, 0, "Logger", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Logger(loggingCategory); }); qmlRegisterType(uri, 1, 0, "BranchesListModel"); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + qmlRegisterAnonymousType(uri, 1); +#else qmlRegisterType(); +#endif } #include "kdevelopdashboarddeclarativeplugin.moc" diff --git a/plugins/welcomepage/kdevwelcomepage.json b/plugins/welcomepage/kdevwelcomepage.json index 947c6e1011..57783f0a75 100644 --- a/plugins/welcomepage/kdevwelcomepage.json +++ b/plugins/welcomepage/kdevwelcomepage.json @@ -1,59 +1,55 @@ { "KPlugin": { "Description": "Provides the welcome page visible in an empty session", "Description[ca@valencia]": "Proporciona la visibilitat de la pàgina de benvinguda en una sessió buida", "Description[ca]": "Proporciona la visibilitat de la pàgina de benvinguda en una sessió buida", "Description[cs]": "Poskytuje uvítací obrazovku viditelnou v prázdném sezení", "Description[de]": "Die in einer leeren Sitzung angezeigte Startseite", - "Description[el]": "Παρέχει τη σελίδα υποδοχής ορατη σε μια κενή συνεδρία", "Description[en_GB]": "Provides the welcome page visible in an empty session", "Description[es]": "Proporciona la página de bienvenida visible en una sesión vacía", "Description[et]": "Pakub tühjas seansis näidatavat tervituslehekülge", "Description[fr]": "Fournit la page d'accueil visible dans une session vide", "Description[gl]": "Fornece a páxina de benvida visíbel nunha sesión baleira.", "Description[it]": "Fornisce la pagina di benvenuto visibile in una sessione vuota", "Description[nl]": "Biedt de welkomst pagina zichtbaar in een lege sessie", "Description[pl]": "Pokazuje stronę powitania widoczną przy pustych sesjach", - "Description[pt]": "Oferece a página de boas-vindas visível numa sessão vazia", + "Description[pt]": "Oferece a página de boas-vindas que fica visível numa sessão vazia", "Description[pt_BR]": "Oferece a página de boas-vindas visível em uma sessão vazia", "Description[sk]": "Poskytuje uvítaciu stránku viditeľnú v prázdnom sedení", "Description[sl]": "Ponuja pozdravni zaslon, ki je viden v prazni seji", "Description[sv]": "Tillhandahåller välkomstsidan synlig i en tom session", "Description[tr]": "Boş oturumda görünen hoşgeldin sayfasını sağlar", "Description[uk]": "Забезпечує роботу сторінки вітання, яку програма показує, якщо сеанс порожній", "Description[x-test]": "xxProvides the welcome page visible in an empty sessionxx", "Description[zh_CN]": "提供空白会话的欢迎页", "Icon": "kdevelop", "Id": "KDevWelcomePage", "Name": "KDevelop Welcome Page", - "Name[ar]": "صفحة ترحيب مطوّرك", "Name[ca@valencia]": "Pàgina de benvinguda del KDevelop", "Name[ca]": "Pàgina de benvinguda del KDevelop", "Name[cs]": "Uvítací stránka KDevelop", "Name[de]": "KDevelop-Startseite", - "Name[el]": "Σελίδα υποδοχής του KDevelop", "Name[en_GB]": "KDevelop Welcome Page", "Name[es]": "Página de bienvenida de KDevelop", "Name[et]": "KDevelopi tervituslehekülg", "Name[fr]": "Page d'accueil de KDevelop", "Name[gl]": "Páxina de benvida de KDevelop", - "Name[hu]": "KDevelop üdvözlőképernyő", "Name[it]": "Pagina di benvenuto di KDevelop", "Name[nl]": "Welkomstpagina van KDevelop", "Name[pl]": "Strona powitalna KDevelop", "Name[pt]": "Página de Boas-Vindas do KDevelop", "Name[pt_BR]": "Página de boas-vindas do KDevelop", "Name[sk]": "Uvítacia stránka KDevelop", "Name[sl]": "Pozdravna stran za KDevelop", "Name[sv]": "KDevelop välkomstsida", "Name[tr]": "KDevelop Karşılama Sayfası", "Name[uk]": "Сторінка вітання KDevelop", "Name[x-test]": "xxKDevelop Welcome Pagexx", "Name[zh_CN]": "KDevelop 欢迎页面", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" }