Skip to content

Commit 04c73e8

Browse files
authored
Upload some files
1 parent bcf28b3 commit 04c73e8

File tree

17 files changed

+19668
-0
lines changed

17 files changed

+19668
-0
lines changed

access/LexillaAccess.cxx

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// SciTE - Scintilla based Text Editor
2+
/** @file LexillaAccess.cxx
3+
** Interface to loadable lexers.
4+
** Maintains a list of lexer library paths and CreateLexer functions.
5+
** If list changes then load all the lexer libraries and find the functions.
6+
** When asked to create a lexer, call each function until one succeeds.
7+
**/
8+
// Copyright 2019 by Neil Hodgson <neilh@scintilla.org>
9+
// The License.txt file describes the conditions under which this software may be distributed.
10+
11+
#include <cstring>
12+
13+
#include <string>
14+
#include <string_view>
15+
#include <vector>
16+
#include <set>
17+
18+
#if !defined(_WIN32)
19+
#include <dlfcn.h>
20+
#else
21+
#include <windows.h>
22+
#endif
23+
24+
#include "ILexer.h"
25+
26+
#include "Lexilla.h"
27+
28+
#include "LexillaAccess.h"
29+
30+
namespace {
31+
32+
#if defined(_WIN32)
33+
typedef FARPROC Function;
34+
typedef HMODULE Module;
35+
constexpr const char *pathSeparator = "\\";
36+
#else
37+
typedef void *Function;
38+
typedef void *Module;
39+
constexpr const char *pathSeparator = "/";
40+
#endif
41+
42+
/// Generic function to convert from a Function(void* or FARPROC) to a function pointer.
43+
/// This avoids undefined and conditionally defined behaviour.
44+
template<typename T>
45+
T FunctionPointer(Function function) noexcept {
46+
static_assert(sizeof(T) == sizeof(function));
47+
T fp {};
48+
memcpy(&fp, &function, sizeof(T));
49+
return fp;
50+
}
51+
52+
#if defined(_WIN32)
53+
54+
std::wstring WideStringFromUTF8(std::string_view sv) {
55+
const int sLength = static_cast<int>(sv.length());
56+
const int cchWide = ::MultiByteToWideChar(CP_UTF8, 0, sv.data(), sLength, nullptr, 0);
57+
std::wstring sWide(cchWide, 0);
58+
::MultiByteToWideChar(CP_UTF8, 0, sv.data(), sLength, sWide.data(), cchWide);
59+
return sWide;
60+
}
61+
62+
#endif
63+
64+
// Turn off deprecation checks as LexillaAccess deprecates its wrapper over
65+
// the deprecated LexerNameFromID. Thus use within LexillaAccess is intentional.
66+
#if defined(__GNUC__) || defined(__clang__)
67+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
68+
#else
69+
#pragma warning(disable: 4996)
70+
#endif
71+
72+
std::string directoryLoadDefault;
73+
std::string lastLoaded;
74+
75+
struct LexLibrary {
76+
Lexilla::CreateLexerFn fnCL;
77+
Lexilla::LexerNameFromIDFn fnLNFI;
78+
Lexilla::GetLibraryPropertyNamesFn fnGLPN;
79+
Lexilla::SetLibraryPropertyFn fnSLP;
80+
std::string nameSpace;
81+
};
82+
std::vector<LexLibrary> libraries;
83+
84+
std::vector<std::string> lexers;
85+
std::vector<std::string> libraryProperties;
86+
87+
Function FindSymbol(Module m, const char *symbol) noexcept {
88+
#if defined(_WIN32)
89+
return ::GetProcAddress(m, symbol);
90+
#else
91+
return dlsym(m, symbol);
92+
#endif
93+
}
94+
95+
Lexilla::CreateLexerFn pCreateLexerDefault = nullptr;
96+
97+
bool NameContainsDot(std::string_view path) noexcept {
98+
for (std::string_view::const_reverse_iterator it = path.crbegin();
99+
it != path.crend(); ++it) {
100+
if (*it == '.')
101+
return true;
102+
if (*it == '/' || *it == '\\')
103+
return false;
104+
}
105+
return false;
106+
}
107+
108+
constexpr bool HasPrefix(std::string_view s, std::string_view prefix) noexcept {
109+
return (s.size() >= prefix.size()) && (prefix == s.substr(0, prefix.size()));
110+
}
111+
112+
}
113+
114+
void Lexilla::SetDefault(CreateLexerFn pCreate) noexcept {
115+
pCreateLexerDefault = pCreate;
116+
}
117+
118+
void Lexilla::SetDefaultDirectory(std::string_view directory) {
119+
directoryLoadDefault = directory;
120+
}
121+
122+
bool Lexilla::Load(std::string_view sharedLibraryPaths) {
123+
if (sharedLibraryPaths == lastLoaded) {
124+
return !libraries.empty();
125+
}
126+
127+
std::string_view paths = sharedLibraryPaths;
128+
lexers.clear();
129+
130+
libraries.clear();
131+
while (!paths.empty()) {
132+
const size_t separator = paths.find_first_of(';');
133+
std::string path(paths.substr(0, separator));
134+
if (separator == std::string::npos) {
135+
paths.remove_prefix(paths.size());
136+
} else {
137+
paths.remove_prefix(separator + 1);
138+
}
139+
if (path == ".") {
140+
if (directoryLoadDefault.empty()) {
141+
path = "";
142+
} else {
143+
path = directoryLoadDefault;
144+
path += pathSeparator;
145+
}
146+
path += LEXILLA_LIB;
147+
}
148+
if (!NameContainsDot(path)) {
149+
// No '.' in name so add extension
150+
path.append(LEXILLA_EXTENSION);
151+
}
152+
#if defined(_WIN32)
153+
// Convert from UTF-8 to wide characters
154+
std::wstring wsPath = WideStringFromUTF8(path);
155+
Module lexillaDL = ::LoadLibraryW(wsPath.c_str());
156+
#else
157+
Module lexillaDL = dlopen(path.c_str(), RTLD_LAZY);
158+
#endif
159+
if (lexillaDL) {
160+
GetLexerCountFn fnLexerCount = FunctionPointer<GetLexerCountFn>(
161+
FindSymbol(lexillaDL, LEXILLA_GETLEXERCOUNT));
162+
GetLexerNameFn fnLexerName = FunctionPointer<GetLexerNameFn>(
163+
FindSymbol(lexillaDL, LEXILLA_GETLEXERNAME));
164+
if (fnLexerCount && fnLexerName) {
165+
const int nLexers = fnLexerCount();
166+
for (int i = 0; i < nLexers; i++) {
167+
char name[100] = "";
168+
fnLexerName(i, name, sizeof(name));
169+
lexers.push_back(name);
170+
}
171+
}
172+
CreateLexerFn fnCL = FunctionPointer<CreateLexerFn>(
173+
FindSymbol(lexillaDL, LEXILLA_CREATELEXER));
174+
LexerNameFromIDFn fnLNFI = FunctionPointer<LexerNameFromIDFn>(
175+
FindSymbol(lexillaDL, LEXILLA_LEXERNAMEFROMID));
176+
GetLibraryPropertyNamesFn fnGLPN = FunctionPointer<GetLibraryPropertyNamesFn>(
177+
FindSymbol(lexillaDL, LEXILLA_GETLIBRARYPROPERTYNAMES));
178+
SetLibraryPropertyFn fnSLP = FunctionPointer<SetLibraryPropertyFn>(
179+
FindSymbol(lexillaDL, LEXILLA_SETLIBRARYPROPERTY));
180+
GetNameSpaceFn fnGNS = FunctionPointer<GetNameSpaceFn>(
181+
FindSymbol(lexillaDL, LEXILLA_GETNAMESPACE));
182+
std::string nameSpace;
183+
if (fnGNS) {
184+
nameSpace = fnGNS();
185+
nameSpace += LEXILLA_NAMESPACE_SEPARATOR;
186+
}
187+
LexLibrary lexLib {
188+
fnCL,
189+
fnLNFI,
190+
fnGLPN,
191+
fnSLP,
192+
nameSpace
193+
};
194+
libraries.push_back(lexLib);
195+
}
196+
}
197+
lastLoaded = sharedLibraryPaths;
198+
199+
std::set<std::string> nameSet;
200+
for (const LexLibrary &lexLib : libraries) {
201+
if (lexLib.fnGLPN) {
202+
const char *cpNames = lexLib.fnGLPN();
203+
if (cpNames) {
204+
std::string_view names = cpNames;
205+
while (!names.empty()) {
206+
const size_t separator = names.find_first_of('\n');
207+
std::string name(names.substr(0, separator));
208+
nameSet.insert(name);
209+
if (separator == std::string::npos) {
210+
names.remove_prefix(names.size());
211+
} else {
212+
names.remove_prefix(separator + 1);
213+
}
214+
}
215+
}
216+
}
217+
}
218+
// Standard Lexilla does not have any properties so can't be added to set.
219+
libraryProperties = std::vector<std::string>(nameSet.begin(), nameSet.end());
220+
221+
return !libraries.empty();
222+
}
223+
224+
Scintilla::ILexer5 *Lexilla::MakeLexer(std::string_view languageName) {
225+
std::string sLanguageName(languageName); // Ensure NUL-termination
226+
// First, try to match namespace then name suffix
227+
for (const LexLibrary &lexLib : libraries) {
228+
if (lexLib.fnCL && !lexLib.nameSpace.empty()) {
229+
if (HasPrefix(languageName, lexLib.nameSpace)) {
230+
Scintilla::ILexer5 *pLexer = lexLib.fnCL(sLanguageName.substr(lexLib.nameSpace.size()).c_str());
231+
if (pLexer) {
232+
return pLexer;
233+
}
234+
}
235+
}
236+
}
237+
// If no match with namespace, try to just match name
238+
for (const LexLibrary &lexLib : libraries) {
239+
if (lexLib.fnCL) {
240+
Scintilla::ILexer5 *pLexer = lexLib.fnCL(sLanguageName.c_str());
241+
if (pLexer) {
242+
return pLexer;
243+
}
244+
}
245+
}
246+
if (pCreateLexerDefault) {
247+
return pCreateLexerDefault(sLanguageName.c_str());
248+
}
249+
#if defined(LEXILLA_STATIC)
250+
Scintilla::ILexer5 *pLexer = CreateLexer(sLanguageName.c_str());
251+
if (pLexer) {
252+
return pLexer;
253+
}
254+
#endif
255+
return nullptr;
256+
}
257+
258+
std::vector<std::string> Lexilla::Lexers() {
259+
return lexers;
260+
}
261+
262+
std::string Lexilla::NameFromID(int identifier) {
263+
for (const LexLibrary &lexLib : libraries) {
264+
if (lexLib.fnLNFI) {
265+
const char *name = lexLib.fnLNFI(identifier);
266+
if (name) {
267+
return name;
268+
}
269+
}
270+
}
271+
return std::string();
272+
}
273+
274+
std::vector<std::string> Lexilla::LibraryProperties() {
275+
return libraryProperties;
276+
}
277+
278+
void Lexilla::SetProperty(const char *key, const char *value) {
279+
for (const LexLibrary &lexLib : libraries) {
280+
if (lexLib.fnSLP) {
281+
lexLib.fnSLP(key, value);
282+
}
283+
}
284+
// Standard Lexilla does not have any properties so don't set.
285+
}

access/LexillaAccess.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SciTE - Scintilla based Text Editor
2+
/** @file LexillaAccess.h
3+
** Interface to loadable lexers.
4+
** This does not depend on SciTE code so can be copied out into other projects.
5+
**/
6+
// Copyright 2019 by Neil Hodgson <neilh@scintilla.org>
7+
// The License.txt file describes the conditions under which this software may be distributed.
8+
9+
#ifndef LEXILLAACCESS_H
10+
#define LEXILLAACCESS_H
11+
12+
namespace Lexilla {
13+
14+
// Directory to load default Lexilla from, commonly the directory of the application.
15+
void SetDefaultDirectory(std::string_view directory);
16+
17+
// Specify CreateLexer when statically linked so no hard dependency in LexillaAccess
18+
// so it doesn't have to be built in two forms - static and dynamic.
19+
void SetDefault(CreateLexerFn pCreate) noexcept;
20+
21+
// sharedLibraryPaths is a ';' separated list of shared libraries to load.
22+
// On Win32 it is treated as UTF-8 and on Unix it is passed to dlopen directly.
23+
// Return true if any shared libraries are loaded.
24+
bool Load(std::string_view sharedLibraryPaths);
25+
26+
Scintilla::ILexer5 *MakeLexer(std::string_view languageName);
27+
28+
std::vector<std::string> Lexers();
29+
[[deprecated]] std::string NameFromID(int identifier);
30+
std::vector<std::string> LibraryProperties();
31+
void SetProperty(const char *key, const char *value);
32+
33+
}
34+
35+
#endif

access/README

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
README for access directory.
2+
3+
LexillaAccess is a module that simplifies using multiple libraries that follow the Lexilla protocol.
4+
5+
It can be compiled into a Lexilla client application.
6+
7+
Applications with complex needs can copy the code and customise it to meet their requirements.
8+
9+
This module is not meant to be compiled into Lexilla.

bin/empty.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This empty files ensures that the directory is created.

0 commit comments

Comments
 (0)