mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-02 19:46:29 +03:00
Add native linter support for XML.
This commit is contained in:
@@ -120,6 +120,14 @@
|
||||
"warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 },
|
||||
"command": "hlint --color=never -j $FILENAME",
|
||||
"url": "https://github.com/ndmitchell/hlint"
|
||||
},
|
||||
{
|
||||
"language": "xml",
|
||||
"file_patterns": ["%.xml$", "%.svg$"],
|
||||
"warning_pattern": "",
|
||||
"command": "xml",
|
||||
"type": "native",
|
||||
"url": "#native"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -66,16 +66,16 @@ std::vector<FeaturesHealth::LangHealth> FeaturesHealth::getHealth( PluginManager
|
||||
if ( linter ) {
|
||||
Linter found = linter->getLinterForLang( def.getLSPName() );
|
||||
if ( !found.command.empty() ) {
|
||||
lang.linter.name = String::split( found.command, ' ' )[0];
|
||||
lang.linter.name =
|
||||
found.isNative ? "native" : String::split( found.command, ' ' )[0];
|
||||
lang.linter.path = Sys::which( lang.linter.name );
|
||||
lang.linter.found = !lang.linter.path.empty();
|
||||
lang.linter.found = !lang.linter.path.empty() || found.isNative;
|
||||
lang.linter.url = found.url;
|
||||
}
|
||||
}
|
||||
|
||||
if ( formatter ) {
|
||||
FormatterPlugin::Formatter found =
|
||||
formatter->getFormatterForLang( def.getLSPName() );
|
||||
FormatterPlugin::Formatter found = formatter->getFormatterForLang( def.getLSPName() );
|
||||
if ( !found.command.empty() ) {
|
||||
lang.formatter.name = found.type == FormatterPlugin::FormatterType::Native
|
||||
? "native"
|
||||
@@ -88,8 +88,7 @@ std::vector<FeaturesHealth::LangHealth> FeaturesHealth::getHealth( PluginManager
|
||||
}
|
||||
|
||||
if ( lsp ) {
|
||||
LSPDefinition found =
|
||||
lsp->getClientManager().getLSPForLang( def.getLSPName() );
|
||||
LSPDefinition found = lsp->getClientManager().getLSPForLang( def.getLSPName() );
|
||||
if ( !found.command.empty() ) {
|
||||
lang.lsp.name = found.name;
|
||||
lang.lsp.url = found.url;
|
||||
|
||||
@@ -34,7 +34,7 @@ class FormatterPlugin : public Plugin {
|
||||
static PluginDefinition Definition() {
|
||||
return {
|
||||
"autoformatter", "Auto Formatter", "Enables the code formatter/prettifier plugin.",
|
||||
FormatterPlugin::New, { 0, 2, 3 }, FormatterPlugin::NewSync };
|
||||
FormatterPlugin::New, { 0, 2, 4 }, FormatterPlugin::NewSync };
|
||||
}
|
||||
|
||||
static Plugin* New( PluginManager* pluginManager );
|
||||
@@ -78,7 +78,7 @@ class FormatterPlugin : public Plugin {
|
||||
std::unordered_map<UICodeEditor*, TextDocument*> mEditorDocs;
|
||||
std::mutex mWorkMutex;
|
||||
std::condition_variable mWorkerCondition;
|
||||
std::map<std::string, std::function<NativeFormatterResult( const std::string& file )>>
|
||||
std::unordered_map<std::string, std::function<NativeFormatterResult( const std::string& file )>>
|
||||
mNativeFormatters;
|
||||
Int32 mWorkersCount{ 0 };
|
||||
std::map<std::string, std::string> mKeyBindings; /* cmd, shortcut */
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <eepp/window/clipboard.hpp>
|
||||
#include <eepp/window/window.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -235,6 +237,20 @@ void LinterPlugin::loadLinterConfig( const std::string& path, bool updateConfigF
|
||||
linter.command = obj["command"].get<std::string>();
|
||||
linter.url = obj.value( "url", "" );
|
||||
|
||||
if ( obj.contains( "type" ) ) {
|
||||
std::string typeStr( obj["type"].get<std::string>() );
|
||||
String::toLowerInPlace( typeStr );
|
||||
String::trimInPlace( typeStr );
|
||||
if ( "native" == typeStr ) {
|
||||
linter.isNative = true;
|
||||
if ( mNativeLinters.find( linter.command ) == mNativeLinters.end() ) {
|
||||
Log::error( "Requested native linter: '%s' does not exists.",
|
||||
linter.command.c_str() );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( obj.contains( "expected_exitcodes" ) ) {
|
||||
auto ee = obj["expected_exitcodes"];
|
||||
if ( ee.is_array() ) {
|
||||
@@ -561,6 +577,8 @@ void LinterPlugin::load( PluginManager* pluginManager ) {
|
||||
[this]( const auto& notification ) -> PluginRequestHandle {
|
||||
return processMessage( notification );
|
||||
} );
|
||||
registerNativeLinters();
|
||||
|
||||
std::vector<std::string> paths;
|
||||
std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" );
|
||||
if ( FileSystem::fileExists( path ) )
|
||||
@@ -797,6 +815,10 @@ void LinterPlugin::runLinter( std::shared_ptr<TextDocument> doc, const Linter& l
|
||||
Clock clock;
|
||||
std::string cmd( linter.command );
|
||||
String::replaceAll( cmd, "$FILENAME", "\"" + path + "\"" );
|
||||
if ( linter.isNative && mNativeLinters.find( cmd ) != mNativeLinters.end() ) {
|
||||
mNativeLinters[cmd]( doc, path );
|
||||
return;
|
||||
}
|
||||
Process process;
|
||||
TextDocument* docPtr = doc.get();
|
||||
ScopedOp op(
|
||||
@@ -829,7 +851,8 @@ void LinterPlugin::runLinter( std::shared_ptr<TextDocument> doc, const Linter& l
|
||||
|
||||
if ( linter.hasNoErrorsExitCode && linter.noErrorsExitCode == returnCode ) {
|
||||
Lock matchesLock( mMatchesMutex );
|
||||
mMatches[doc.get()] = {};
|
||||
std::map<Int64, std::vector<LinterMatch>> empty;
|
||||
setMatches( doc.get(), MatchOrigin::Linter, empty );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1318,4 +1341,56 @@ bool LinterPlugin::onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* menu,
|
||||
return false;
|
||||
}
|
||||
|
||||
void LinterPlugin::registerNativeLinter(
|
||||
const std::string& cmd,
|
||||
const std::function<void( std::shared_ptr<TextDocument>, const std::string& )>& nativeLinter ) {
|
||||
mNativeLinters[cmd] = nativeLinter;
|
||||
}
|
||||
|
||||
void LinterPlugin::unregisterNativeLinter( const std::string& cmd ) {
|
||||
mNativeLinters.erase( cmd );
|
||||
}
|
||||
|
||||
static size_t countLines( const std::string_view& text ) {
|
||||
const char* startPtr = text.data();
|
||||
const char* endPtr = text.data() + text.size();
|
||||
size_t count = 0;
|
||||
if ( startPtr != endPtr ) {
|
||||
count = 1 + *startPtr == '\n' ? 1 : 0;
|
||||
while ( ++startPtr && startPtr != endPtr )
|
||||
count += ( '\n' == *startPtr ) ? 1 : 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void LinterPlugin::registerNativeLinters() {
|
||||
if ( !mNativeLinters.empty() )
|
||||
return;
|
||||
mNativeLinters["xml"] = [this]( std::shared_ptr<TextDocument> doc, const std::string& path ) {
|
||||
pugi::xml_document xmlDoc;
|
||||
pugi::xml_parse_result result = xmlDoc.load_file( path.c_str() );
|
||||
std::map<Int64, std::vector<LinterMatch>> matches;
|
||||
if ( !result ) {
|
||||
std::string file;
|
||||
FileSystem::fileGet( path, file );
|
||||
std::string_view filesv{ file };
|
||||
Int64 line = countLines( filesv.substr( 0, result.offset ) );
|
||||
Int64 offset = 0;
|
||||
auto lastNL = filesv.substr( 0, result.offset ).find_last_of( '\n' );
|
||||
if ( lastNL != std::string_view::npos )
|
||||
offset = result.offset - lastNL;
|
||||
LinterMatch match;
|
||||
match.range = { { line, offset }, { line, offset } };
|
||||
match.range = { doc->nextWordBoundary( match.range.start(), false ),
|
||||
doc->previousWordBoundary( match.range.start(), false ) };
|
||||
match.text = result.description();
|
||||
match.type = getLinterTypeFromSeverity( LSPDiagnosticSeverity::Error );
|
||||
match.lineCache = doc->line( match.range.start().line() ).getHash();
|
||||
match.origin = MatchOrigin::Linter;
|
||||
matches[line] = { match };
|
||||
}
|
||||
setMatches( doc.get(), MatchOrigin::Linter, matches );
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -38,6 +38,7 @@ struct Linter {
|
||||
std::vector<Int64> expectedExitCodes{};
|
||||
int noErrorsExitCode{ 0 };
|
||||
std::string url;
|
||||
bool isNative{ false };
|
||||
};
|
||||
|
||||
struct LinterMatch {
|
||||
@@ -46,7 +47,7 @@ struct LinterMatch {
|
||||
LinterType type{ LinterType::Error };
|
||||
String::HashType lineCache{ 0 };
|
||||
MatchOrigin origin{ MatchOrigin::Linter };
|
||||
std::map<UICodeEditor*, Rectf> box;
|
||||
std::unordered_map<UICodeEditor*, Rectf> box;
|
||||
LSPDiagnostic diagnostic;
|
||||
};
|
||||
|
||||
@@ -58,7 +59,7 @@ class LinterPlugin : public Plugin {
|
||||
"Use static code analysis tool used to flag programming errors, bugs, "
|
||||
"stylistic errors, and suspicious constructs.",
|
||||
LinterPlugin::New,
|
||||
{ 0, 2, 3 },
|
||||
{ 0, 2, 4 },
|
||||
LinterPlugin::NewSync };
|
||||
}
|
||||
|
||||
@@ -111,6 +112,12 @@ class LinterPlugin : public Plugin {
|
||||
virtual bool onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* menu,
|
||||
const Vector2i& position, const Uint32& flags );
|
||||
|
||||
void registerNativeLinter( const std::string& cmd,
|
||||
const std::function<void( std::shared_ptr<TextDocument> doc,
|
||||
const std::string& file )>& nativeLinter );
|
||||
|
||||
void unregisterNativeLinter( const std::string& cmd );
|
||||
|
||||
protected:
|
||||
std::vector<Linter> mLinters;
|
||||
std::unordered_map<UICodeEditor*, std::vector<Uint32>> mEditors;
|
||||
@@ -127,6 +134,9 @@ class LinterPlugin : public Plugin {
|
||||
std::map<std::string, std::string> mKeyBindings; /* cmd, shortcut */
|
||||
std::mutex mRunningProcessesMutex;
|
||||
std::unordered_map<TextDocument*, Process*> mRunningProcesses;
|
||||
std::unordered_map<std::string, std::function<void( std::shared_ptr<TextDocument> doc,
|
||||
const std::string& file )>>
|
||||
mNativeLinters;
|
||||
|
||||
bool mHoveringMatch{ false };
|
||||
bool mEnableLSPDiagnostics{ true };
|
||||
@@ -177,6 +187,8 @@ class LinterPlugin : public Plugin {
|
||||
void goToNextError( UICodeEditor* editor );
|
||||
|
||||
void goToPrevError( UICodeEditor* editor );
|
||||
|
||||
void registerNativeLinters();
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
Reference in New Issue
Block a user