Add native linter support for XML.

This commit is contained in:
Martín Lucas Golini
2024-03-10 15:53:07 -03:00
parent 6c94ea50d9
commit 7d12ceeabb
5 changed files with 105 additions and 11 deletions

View File

@@ -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"
}
]
}

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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

View File

@@ -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