|
@@ -3,6 +3,9 @@
|
|
|
#include "lumacs/lua_api.hpp"
|
|
#include "lumacs/lua_api.hpp"
|
|
|
#include "lumacs/keybinding.hpp"
|
|
#include "lumacs/keybinding.hpp"
|
|
|
#include <iostream>
|
|
#include <iostream>
|
|
|
|
|
+#include <filesystem>
|
|
|
|
|
+#include <vector>
|
|
|
|
|
+#include <algorithm>
|
|
|
|
|
|
|
|
// Check if GTK is enabled in build
|
|
// Check if GTK is enabled in build
|
|
|
#ifdef LUMACS_WITH_GTK
|
|
#ifdef LUMACS_WITH_GTK
|
|
@@ -867,6 +870,78 @@ protected:
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
// TODO: File completion for FindFile
|
|
// TODO: File completion for FindFile
|
|
|
|
|
+ if (mode_ == Mode::FindFile) {
|
|
|
|
|
+ std::string input = command_buffer_;
|
|
|
|
|
+ namespace fs = std::filesystem;
|
|
|
|
|
+
|
|
|
|
|
+ fs::path search_path;
|
|
|
|
|
+ std::string prefix;
|
|
|
|
|
+
|
|
|
|
|
+ // Determine directory and prefix
|
|
|
|
|
+ if (input.empty()) {
|
|
|
|
|
+ search_path = fs::current_path();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fs::path input_path(input);
|
|
|
|
|
+ if (fs::is_directory(input_path) && input.back() == '/') {
|
|
|
|
|
+ search_path = input_path;
|
|
|
|
|
+ prefix = "";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (input_path.has_parent_path()) {
|
|
|
|
|
+ search_path = input_path.parent_path();
|
|
|
|
|
+ if (search_path.string().empty()) search_path = fs::current_path();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ search_path = fs::current_path();
|
|
|
|
|
+ }
|
|
|
|
|
+ prefix = input_path.filename().string();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::vector<std::string> matches;
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (fs::exists(search_path) && fs::is_directory(search_path)) {
|
|
|
|
|
+ for (const auto& entry : fs::directory_iterator(search_path)) {
|
|
|
|
|
+ std::string filename = entry.path().filename().string();
|
|
|
|
|
+ if (filename.find(prefix) == 0) {
|
|
|
|
|
+ if (fs::is_directory(entry.path())) {
|
|
|
|
|
+ matches.push_back(filename + "/");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ matches.push_back(filename);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (...) {
|
|
|
|
|
+ // Ignore permission errors etc
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (matches.size() == 1) {
|
|
|
|
|
+ // Append the difference
|
|
|
|
|
+ std::string completion = matches[0].substr(prefix.length());
|
|
|
|
|
+ command_buffer_ += completion;
|
|
|
|
|
+ } else if (matches.size() > 1) {
|
|
|
|
|
+ // Find common prefix of MATCHES (not including full path)
|
|
|
|
|
+ std::string common = matches[0];
|
|
|
|
|
+ for (size_t i = 1; i < matches.size(); ++i) {
|
|
|
|
|
+ const std::string& s = matches[i];
|
|
|
|
|
+ size_t j = 0;
|
|
|
|
|
+ while (j < common.size() && j < s.size() && common[j] == s[j]) {
|
|
|
|
|
+ j++;
|
|
|
|
|
+ }
|
|
|
|
|
+ common = common.substr(0, j);
|
|
|
|
|
+ }
|
|
|
|
|
+ // Append difference between common prefix and current input prefix
|
|
|
|
|
+ if (common.length() > prefix.length()) {
|
|
|
|
|
+ command_buffer_ += common.substr(prefix.length());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ message_line_ = "Multiple matches";
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ message_line_ = "No match";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (content_widget_) queue_redraw_all_windows(content_widget_);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (key_name == "Return") {
|
|
if (key_name == "Return") {
|