|
@@ -373,6 +373,52 @@
|
|
|
// Remove unwanted shortcuts
|
|
|
editor.commands.bindKey("Ctrl-T", null); // Disable new browser tab shortcut
|
|
|
|
|
|
+// Levenshtein distance algorithm
|
|
|
+function levenshteinDistance(a, b) {
|
|
|
+ let m = a.length, n = b.length;
|
|
|
+ let dp = [];
|
|
|
+ for (let i = 0; i <= m; i++) {
|
|
|
+ dp[i] = [i];
|
|
|
+ }
|
|
|
+ for (let j = 0; j <= n; j++) {
|
|
|
+ dp[0][j] = j;
|
|
|
+ }
|
|
|
+ for (let i = 1; i <= m; i++) {
|
|
|
+ for (let j = 1; j <= n; j++) {
|
|
|
+ if (a[i - 1] === b[j - 1]) {
|
|
|
+ dp[i][j] = dp[i - 1][j - 1];
|
|
|
+ } else {
|
|
|
+ dp[i][j] = Math.min(
|
|
|
+ dp[i - 1][j] + 1, // deletion
|
|
|
+ dp[i][j - 1] + 1, // insertion
|
|
|
+ dp[i - 1][j - 1] + 1 // substitution
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return dp[m][n];
|
|
|
+}
|
|
|
+
|
|
|
+// Example function to find the closest matching class key
|
|
|
+function findClosestMatch(refName, phpMethods) {
|
|
|
+ let bestMatch = null;
|
|
|
+ let bestDistance = Infinity;
|
|
|
+ let lowerRef = refName.toLowerCase();
|
|
|
+
|
|
|
+ // Loop through all classes in phpMethods
|
|
|
+ for (let key in phpMethods) {
|
|
|
+ // Assume the simple class name is the last segment after a backslash
|
|
|
+ let parts = key.split("\\");
|
|
|
+ let simpleName = parts[parts.length - 1].toLowerCase();
|
|
|
+ let distance = levenshteinDistance(lowerRef, simpleName);
|
|
|
+ if (distance < bestDistance) {
|
|
|
+ bestDistance = distance;
|
|
|
+ bestMatch = key;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return bestMatch;
|
|
|
+}
|
|
|
+
|
|
|
// Function to fetch PHP class methods using fetch() with promises
|
|
|
async function fetch_php_methods() {
|
|
|
try {
|
|
@@ -387,79 +433,84 @@ async function fetch_php_methods() {
|
|
|
|
|
|
// Initialize ACE auto-completion after fetching PHP methods
|
|
|
async function init_ace_completion() {
|
|
|
- let phpMethods = await fetch_php_methods();
|
|
|
+ const php_methods = await fetch_php_methods();
|
|
|
|
|
|
// Custom completer for PHP class methods
|
|
|
var php_class_completer = {
|
|
|
getCompletions: function(editor, session, pos, prefix, callback) {
|
|
|
+
|
|
|
+ // Define current_class_name by extracting the basename of the current file without extension.
|
|
|
+ var current_file_path = document.getElementById('current_file').value;
|
|
|
+ var current_file_name = current_file_path.split('/').pop();
|
|
|
+
|
|
|
+ // Remove the extension (everything after the last dot)
|
|
|
+ var current_class_name = current_file_name.replace(/\.[^/.]+$/, "");
|
|
|
+
|
|
|
// Get the current line text
|
|
|
var line = session.getLine(pos.row);
|
|
|
|
|
|
- // Use regex to detect object (->) or static (::) access
|
|
|
- const objectMatch = line.match(/(\w+)\s*->\s*\w*$/);
|
|
|
- const staticMatch = line.match(/(\w+)::\w*$/);
|
|
|
-
|
|
|
- // Extract the referenced class name (simple name)
|
|
|
- var ref_name = objectMatch ? objectMatch[1] : (staticMatch ? staticMatch[1] : null);
|
|
|
- if (!ref_name) return callback(null, []);
|
|
|
+ // Use regex to detect object (->) or static (::) access.
|
|
|
+ // This regex captures either "$this" or any other word.
|
|
|
+ const object_match = line.match(/(\$this|\w+)\s*->\s*\w*$/);
|
|
|
+ const static_match = line.match(/(self|static|\w+)::\w*$/);
|
|
|
|
|
|
- // Try to match the simple class name (case-insensitive) with one of the keys in phpMethods.
|
|
|
- // The keys in phpMethods may be fully-qualified names (with namespaces).
|
|
|
- var matched_class = null;
|
|
|
- for (var key in phpMethods) {
|
|
|
- // Get the simple class name from the key
|
|
|
- var parts = key.split("\\");
|
|
|
- var simple_name = parts[parts.length - 1];
|
|
|
- if (simple_name.toLowerCase() === ref_name.toLowerCase()) {
|
|
|
- matched_class = key;
|
|
|
- break;
|
|
|
- }
|
|
|
+ // Extract the referenced name; if it's "$this", use the current_class_name.
|
|
|
+ var ref_name = object_match ? object_match[1] : (static_match ? static_match[1] : null);
|
|
|
+ if (ref_name === '$this' | ref_name === 'self' | ref_name === 'static') {
|
|
|
+ ref_name = current_class_name;
|
|
|
}
|
|
|
|
|
|
- // If no matching class is found, return an empty list.
|
|
|
+ // If not a class, maybe a user function; use the prefix.
|
|
|
+ if (prefix.length > 0) ref_name = prefix;
|
|
|
+
|
|
|
+ if (!ref_name) return callback(null, []);
|
|
|
+
|
|
|
+ // Find the closest matching class using fuzzy matching.
|
|
|
+ var matched_class = findClosestMatch(ref_name, php_methods);
|
|
|
if (!matched_class) return callback(null, []);
|
|
|
|
|
|
// Map the methods of the matched class into completions.
|
|
|
- var completions = phpMethods[matched_class].map(function(method) {
|
|
|
- if (staticMatch !== null) {
|
|
|
- if (method.static) {
|
|
|
- return {
|
|
|
- caption: method.name + method.params,
|
|
|
- snippet: method.name + method.params.replace(/\$/g, "\\$"),
|
|
|
- meta: matched_class,
|
|
|
- docHTML: method.doc ? method.doc : "No Documentation"
|
|
|
- };
|
|
|
- } else {
|
|
|
- return {};
|
|
|
- }
|
|
|
+ var completions = php_methods[matched_class].map(function(method) {
|
|
|
+ // If static syntax is used but the method is not static, skip it.
|
|
|
+ if (static_match !== null && !method.static) {
|
|
|
+ return {};
|
|
|
}
|
|
|
- //you can call a static method on an object instance because php is like that
|
|
|
- return {
|
|
|
+
|
|
|
+ // Map the item to the documentation
|
|
|
+ var item = {
|
|
|
caption: method.name + method.params,
|
|
|
snippet: method.name + method.params.replace(/\$/g, "\\$"),
|
|
|
- meta: matched_class,
|
|
|
+ meta: method.meta,
|
|
|
docHTML: method.doc ? method.doc : "No Documentation"
|
|
|
};
|
|
|
+
|
|
|
+ // TODO: fix italics
|
|
|
+ // Use italics in pop-up for static methods
|
|
|
+ if (method.static) {
|
|
|
+ item.className = "ace_static_method";
|
|
|
+ }
|
|
|
+
|
|
|
+ //return the mapped json object
|
|
|
+ return item;
|
|
|
});
|
|
|
|
|
|
callback(null, completions);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // Initialize ACE Editor (assumes 'editor' is already created)
|
|
|
+ // Initialize ACE Editor
|
|
|
ace.require("ace/ext/language_tools");
|
|
|
|
|
|
- // Override the default completions with our custom completer
|
|
|
+ // Replace the current list of completions with our custom one so we don't have every single word in the document listed as a completion option
|
|
|
editor.completers = [php_class_completer];
|
|
|
|
|
|
// Ensure font size is set
|
|
|
- document.getElementById('editor').style.fontSize='<?=$setting_size?>';
|
|
|
- focus_editor();
|
|
|
-
|
|
|
+ document.getElementById('editor').style.fontSize = '<?=$setting_size?>';
|
|
|
+ focus_editor();
|
|
|
}
|
|
|
|
|
|
// Run auto-completion setup
|
|
|
-init_ace_completion();
|
|
|
+ init_ace_completion();
|
|
|
</script>
|
|
|
</body>
|
|
|
<script>
|