Skip to content

Commit 0f8af70

Browse files
authored
Merge pull request #237 from glennsarti/use-object-cache-take2
(GH-236) Use an in memory and persistent object cache for Puppet assets
2 parents 8fdfb78 + 57c74c9 commit 0f8af70

File tree

22 files changed

+1188
-288
lines changed

22 files changed

+1188
-288
lines changed

client/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
88

99
- ([GH-225](https://github.com/jpogran/puppet-vscode/issues/225)) Readd Local Workspace comand line option
1010
- ([GH-231](https://github.com/jpogran/puppet-vscode/issues/231)) Make Document Validation asynchronous
11+
- ([GH-236](https://github.com/jpogran/puppet-vscode/issues/236)) Remove the preload option
12+
- ([GH-236](https://github.com/jpogran/puppet-vscode/issues/236)) Add experimental file cache option
1113

1214
## 0.9.0 - 2018-02-01
1315

client/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@
288288
"default": 10,
289289
"description": "The timeout to connect to the local Puppet Language Server"
290290
},
291-
"puppet.languageserver.preLoadPuppet": {
291+
"puppet.languageserver.filecache.enable": {
292292
"type": "boolean",
293-
"default": true,
294-
"description": "Initalize Puppet and Facter when local Puppet Language Server starts"
293+
"default": false,
294+
"description": "(Experimental) Enable a persistent file cache for Puppet objects in the Language Server"
295295
},
296296
"puppet.languageserver.debugFilePath": {
297297
"type": "string",

client/src/configuration.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ import { IConnectionConfiguration, ConnectionType } from './interfaces';
66
import { ConnectionManager } from './connection';
77

88
export class ConnectionConfiguration implements IConnectionConfiguration {
9-
public type: ConnectionType = ConnectionType.Unknown;
9+
public type: ConnectionType = ConnectionType.Unknown;
1010
public host: string = undefined;
1111
public port: number = undefined;
1212
public timeout: number = undefined;
13-
public preLoadPuppet: boolean = undefined;
13+
public enableFileCache: boolean = undefined;
1414
public debugFilePath: string = undefined;
1515
public puppetAgentDir: string = undefined;
1616

1717
constructor(context: vscode.ExtensionContext) {
1818
let config = vscode.workspace.getConfiguration('puppet');
1919

20-
this.host = config['languageserver']['address'];
21-
this.port = config['languageserver']['port'];
22-
this.timeout = config['languageserver']['timeout'];
23-
this.preLoadPuppet = config['languageserver']['preLoadPuppet'];
24-
this.debugFilePath = config['languageserver']['debugFilePath'];
25-
20+
this.host = config['languageserver']['address'];
21+
this.port = config['languageserver']['port'];
22+
this.timeout = config['languageserver']['timeout'];
23+
this.enableFileCache = config['languageserver']['filecache']['enable'];
24+
this.debugFilePath = config['languageserver']['debugFilePath'];
25+
2626
this.puppetAgentDir = config['puppetAgentDir'];
2727
}
2828
}

client/src/connection.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ export class ConnectionManager implements IConnectionManager {
170170
}
171171
args.push('--port=' + this.connectionConfiguration.port);
172172
args.push('--timeout=' + this.connectionConfiguration.timeout);
173-
if (this.connectionConfiguration.preLoadPuppet == false) { args.push('--no-preload'); }
173+
if (this.connectionConfiguration.enableFileCache) {
174+
args.push('--enable-file-cache');
175+
}
174176
if ((this.connectionConfiguration.debugFilePath != undefined) && (this.connectionConfiguration.debugFilePath != '')) {
175177
args.push('--debug=' + this.connectionConfiguration.debugFilePath);
176178
}
@@ -291,10 +293,10 @@ export class ConnectionManager implements IConnectionManager {
291293

292294
connectionManager.languageClient.sendRequest(messages.PuppetVersionRequest.type).then((versionDetails) => {
293295
lastVersionResponse = versionDetails
294-
if (!connectionManager.connectionConfiguration.preLoadPuppet || (versionDetails.factsLoaded &&
296+
if (versionDetails.factsLoaded &&
295297
versionDetails.functionsLoaded &&
296298
versionDetails.typesLoaded &&
297-
versionDetails.classesLoaded)) {
299+
versionDetails.classesLoaded) {
298300
clearInterval(handle);
299301
this.setConnectionStatus(lastVersionResponse.puppetVersion, ConnectionStatus.Running);
300302
resolve();

client/src/debugAdapter.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class DebugConfiguration implements IConnectionConfiguration {
4343
public host: string = "127.0.0.1";
4444
public port: number = 8082;
4545
public timeout: number = 10;
46-
public preLoadPuppet: boolean = false;
46+
public enableFileCache: boolean = undefined;
4747
public debugFilePath: string; // = "STDOUT";
4848
public puppetAgentDir: string;
4949
}
@@ -117,7 +117,7 @@ function startDebugServer(config:DebugConfiguration, debugLogger: ILogger) {
117117
if (localServer == null) { localServer = RubyHelper.getRubyEnvFromPuppetAgent(rubyfile, config, debugLogger); }
118118
// Commented out for the moment. This will be enabled once the configuration and exact user story is figured out.
119119
// if (localServer == null) { localServer = RubyHelper.getRubyEnvFromPDK(rubyfile, config, debugLogger); }
120-
120+
121121
if (localServer == null) {
122122
sendErrorMessage("Unable to find a valid ruby environment");
123123
process.exit(255);
@@ -145,7 +145,7 @@ function startDebugging(config:DebugConfiguration, debugLogger:ILogger) {
145145
debugServerProc.on('close', (exitCode) => {
146146
debugLogger.debug("Debug server terminated with exit code: " + exitCode);
147147
debugServerProc.kill();
148-
process.exit(exitCode);
148+
process.exit(exitCode);
149149
});
150150

151151
debugServerProc.on('error', (data) => {
@@ -155,19 +155,19 @@ function startDebugging(config:DebugConfiguration, debugLogger:ILogger) {
155155
process.on('SIGTERM', () => {
156156
debugLogger.debug("Received SIGTERM");
157157
debugServerProc.kill();
158-
process.exit(0);
158+
process.exit(0);
159159
});
160160

161161
process.on('SIGHUP', () => {
162162
debugLogger.debug("Received SIGHUP");
163163
debugServerProc.kill();
164-
process.exit(0);
164+
process.exit(0);
165165
});
166166

167167
process.on('exit', () => {
168168
debugLogger.debug("Received Exit");
169169
debugServerProc.kill();
170-
process.exit(0);
170+
process.exit(0);
171171
});
172172
}
173173

client/src/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface IConnectionConfiguration {
2121
host: string;
2222
port: number;
2323
timeout: number;
24-
preLoadPuppet: boolean;
24+
enableFileCache: boolean;
2525
debugFilePath: string;
2626
puppetAgentDir: string;
2727
}

server/lib/puppet-languageserver.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def self.parse(options)
5656
args[:connection_timeout] = timeout.to_i
5757
end
5858

59-
opts.on('-d', '--no-preload', 'Do not preload Puppet information when the language server starts. Default is to preload') do |_misc|
59+
opts.on('-d', '--no-preload', '** DEPRECATED ** Do not preload Puppet information when the language server starts. Default is to preload') do |_misc|
60+
puts '** WARNING ** Using "--no-preload" may cause Puppet Type loading to be incomplete.'
6061
args[:preload_puppet] = false
6162
end
6263

@@ -72,6 +73,12 @@ def self.parse(options)
7273
args[:stdio] = true
7374
end
7475

76+
opts.on('--enable-file-cache', 'Enables the file system cache for Puppet Objects (types, class etc.)') do |_misc|
77+
args[:cache] = {
78+
:persistent_cache => :file
79+
}
80+
end
81+
7582
opts.on('--local-workspace=PATH', 'The workspace or file path that will be used to provide module-specific functionality. Default is no workspace path.') do |path|
7683
args[:workspace] = path
7784
end
@@ -101,6 +108,9 @@ def self.init_puppet(options)
101108
log_message(:info, "Language Server is v#{PuppetVSCode.version}")
102109
log_message(:info, "Using Puppet v#{Puppet.version}")
103110

111+
log_message(:info, 'Initializing Puppet Helper Cache...')
112+
PuppetLanguageServer::PuppetHelper.configure_cache(options[:cache])
113+
104114
log_message(:info, 'Initializing settings...')
105115
if options[:fast_start_tcpserver]
106116
Thread.new do
@@ -118,12 +128,12 @@ def self.init_puppet_worker(options)
118128

119129
log_message(:info, "Using Facter v#{Facter.version}")
120130
if options[:preload_puppet]
131+
log_message(:info, 'Preloading Puppet Types (Sync)...')
132+
PuppetLanguageServer::PuppetHelper.load_types
133+
121134
log_message(:info, 'Preloading Facter (Async)...')
122135
PuppetLanguageServer::FacterHelper.load_facts_async
123136

124-
log_message(:info, 'Preloading Puppet Types (Async)...')
125-
PuppetLanguageServer::PuppetHelper.load_types_async
126-
127137
log_message(:info, 'Preloading Functions (Async)...')
128138
PuppetLanguageServer::PuppetHelper.load_functions_async
129139

server/lib/puppet-languageserver/completion_provider.rb

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,29 +44,26 @@ def self.complete(content, line_num, char_num)
4444
# We are inside a resource definition. Should display all available
4545
# properities and parameters.
4646

47-
# TODO: Should really cache all of the resources and params/props for quick
48-
# searching and then only actually instatiate when needed. For the moment,
49-
# instantiate all the things!
50-
51-
item_type = Puppet::Type.type(item.type_name.value)
47+
item_type = PuppetLanguageServer::PuppetHelper.get_type(item.type_name.value)
5248
# Add Parameters
53-
item_type.parameters.each do |param|
54-
items << LanguageServer::CompletionItem.create('label' => param.to_s,
49+
item_type.parameters.each_key do |name|
50+
items << LanguageServer::CompletionItem.create('label' => name.to_s,
5551
'kind' => LanguageServer::COMPLETIONITEMKIND_PROPERTY,
5652
'detail' => 'Parameter',
5753
'data' => { 'type' => 'resource_parameter',
58-
'param' => param.to_s,
54+
'param' => name.to_s,
5955
'resource_type' => item.type_name.value })
6056
end
6157
# Add Properties
62-
item_type.properties.each do |prop|
63-
items << LanguageServer::CompletionItem.create('label' => prop.name.to_s,
58+
item_type.properties.each_key do |name|
59+
items << LanguageServer::CompletionItem.create('label' => name.to_s,
6460
'kind' => LanguageServer::COMPLETIONITEMKIND_PROPERTY,
6561
'detail' => 'Property',
6662
'data' => { 'type' => 'resource_property',
67-
'prop' => prop.name.to_s,
63+
'prop' => name.to_s,
6864
'resource_type' => item.type_name.value })
6965
end
66+
# TODO: What about meta parameters?
7067
end
7168

7269
LanguageServer::CompletionList.create('isIncomplete' => incomplete,
@@ -110,12 +107,12 @@ def self.all_resources(&block)
110107

111108
def self.all_statement_functions(&block)
112109
# Find functions which don't return values i.e. statements
113-
PuppetLanguageServer::PuppetHelper.functions.select { |_key, obj| obj[:type] == :statement }.each_key do |key|
114-
item = LanguageServer::CompletionItem.create('label' => key.to_s,
110+
PuppetLanguageServer::PuppetHelper.filtered_function_names { |_name, data| data.type == :statement }.each do |name|
111+
item = LanguageServer::CompletionItem.create('label' => name.to_s,
115112
'kind' => LanguageServer::COMPLETIONITEMKIND_FUNCTION,
116113
'detail' => 'Function',
117114
'data' => { 'type' => 'function',
118-
'name' => key.to_s })
115+
'name' => name.to_s })
119116
block.call(item) if block
120117
end
121118
end
@@ -162,32 +159,36 @@ def self.resolve(completion_item)
162159

163160
when 'function'
164161
item_type = PuppetLanguageServer::PuppetHelper.function(data['name'])
165-
completion_item['documentation'] = item_type[:doc] unless item_type[:doc].nil?
162+
completion_item['documentation'] = item_type.doc unless item_type.doc.nil?
166163
completion_item['insertText'] = "#{data['name']}(${1:value}"
167-
(2..item_type[:arity]).each do |index|
164+
(2..item_type.arity).each do |index|
168165
completion_item['insertText'] += ", ${#{index}:value}"
169166
end
170167
completion_item['insertText'] += ')'
171168
completion_item['insertTextFormat'] = LanguageServer::INSERTTEXTFORMAT_SNIPPET
172169

173170
when 'resource_type'
174-
item_type = Puppet::Type.type(data['name'])
171+
item_type = PuppetLanguageServer::PuppetHelper.get_type(data['name'])
175172
# TODO: More things?
176173
completion_item['documentation'] = item_type.doc unless item_type.doc.nil?
177174
completion_item['insertText'] = "#{data['name']} { '${1:title}':\n\tensure => '${2:present}'\n}"
178175
completion_item['insertTextFormat'] = LanguageServer::INSERTTEXTFORMAT_SNIPPET
179176
when 'resource_parameter'
180-
item_type = Puppet::Type.type(data['resource_type'])
181-
param_type = item_type.attrclass(data['param'].intern)
182-
# TODO: More things?
183-
completion_item['documentation'] = param_type.doc unless param_type.doc.nil?
184-
completion_item['insertText'] = "#{data['param']} => "
177+
item_type = PuppetLanguageServer::PuppetHelper.get_type(data['resource_type'])
178+
param_type = item_type.parameters[data['param'].intern]
179+
unless param_type.nil?
180+
# TODO: More things?
181+
completion_item['documentation'] = param_type[:doc] unless param_type[:doc].nil?
182+
completion_item['insertText'] = "#{data['param']} => "
183+
end
185184
when 'resource_property'
186-
item_type = Puppet::Type.type(data['resource_type'])
187-
prop_type = item_type.attrclass(data['prop'].intern)
188-
# TODO: More things?
189-
completion_item['documentation'] = prop_type.doc unless prop_type.doc.nil?
190-
completion_item['insertText'] = "#{data['prop']} => "
185+
item_type = PuppetLanguageServer::PuppetHelper.get_type(data['resource_type'])
186+
prop_type = item_type.properties[data['prop'].intern]
187+
unless prop_type.nil?
188+
# TODO: More things?
189+
completion_item['documentation'] = prop_type[:doc] unless prop_type[:doc].nil?
190+
completion_item['insertText'] = "#{data['prop']} => "
191+
end
191192
end
192193

193194
LanguageServer::CompletionItem.create(completion_item)

server/lib/puppet-languageserver/definition_provider.rb

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ def self.find_definition(content, line_num, char_num)
6565
def self.type_or_class(resource_name)
6666
# Strip the leading double-colons for root resource names
6767
resource_name = resource_name.slice(2, resource_name.length - 2) if resource_name.start_with?('::')
68-
location = PuppetLanguageServer::PuppetHelper.type_load_info(resource_name)
69-
location = PuppetLanguageServer::PuppetHelper.class_load_info(resource_name) if location.nil?
70-
unless location.nil?
68+
item = PuppetLanguageServer::PuppetHelper.get_type(resource_name)
69+
item = PuppetLanguageServer::PuppetHelper.get_class(resource_name) if item.nil?
70+
unless item.nil?
7171
return LanguageServer::Location.create(
72-
'uri' => 'file:///' + location['source'],
73-
'fromline' => location['line'],
72+
'uri' => 'file:///' + item.source,
73+
'fromline' => item.line,
7474
'fromchar' => 0,
75-
'toline' => location['line'],
75+
'toline' => item.line,
7676
'tochar' => 1024
7777
)
7878
end
@@ -81,17 +81,15 @@ def self.type_or_class(resource_name)
8181
private_class_method :type_or_class
8282

8383
def self.function_name(func_name)
84-
location = PuppetLanguageServer::PuppetHelper.function_load_info(func_name)
85-
unless location.nil?
86-
return LanguageServer::Location.create(
87-
'uri' => 'file:///' + location['source'],
88-
'fromline' => location['line'],
89-
'fromchar' => 0,
90-
'toline' => location['line'],
91-
'tochar' => 1024
92-
)
93-
end
94-
nil
84+
item = PuppetLanguageServer::PuppetHelper.function(func_name)
85+
return nil if item.nil? || item.source.nil? || item.line.nil?
86+
LanguageServer::Location.create(
87+
'uri' => 'file:///' + item.source,
88+
'fromline' => item.line,
89+
'fromchar' => 0,
90+
'toline' => item.line,
91+
'tochar' => 1024
92+
)
9593
end
9694
private_class_method :function_name
9795
end

server/lib/puppet-languageserver/hover_provider.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ def self.resolve(content, line_num, char_num)
4040
item_type = PuppetLanguageServer::PuppetHelper.get_type(path[distance_up_ast - 1].type_name.value)
4141
raise "#{path[distance_up_ast - 1].type_name.value} is not a valid puppet type" if item_type.nil?
4242
# Check if it's a property
43-
attribute = item_type.validproperty?(item.attribute_name)
43+
attribute = item_type.properties.keys.include?(item.attribute_name.intern)
4444
if attribute != false
4545
content = get_attribute_property_content(item_type, item.attribute_name.intern)
46-
elsif item_type.validparameter?(item.attribute_name.intern)
46+
elsif item_type.parameters.keys.include?(item.attribute_name.intern)
4747
content = get_attribute_parameter_content(item_type, item.attribute_name.intern)
4848
end
4949

@@ -102,17 +102,17 @@ def self.get_fact_content(factname)
102102
end
103103

104104
def self.get_attribute_parameter_content(item_type, param)
105-
param_type = item_type.attrclass(param)
105+
param_type = item_type.parameters[param]
106106
content = "**#{param}** Parameter"
107-
content += "\n\n#{param_type.doc}" unless param_type.doc.nil?
107+
content += "\n\n#{param_type[:doc]}" unless param_type[:doc].nil?
108108
content
109109
end
110110

111111
def self.get_attribute_property_content(item_type, property)
112-
prop_type = item_type.attrclass(property)
112+
prop_type = item_type.properties[property]
113113
content = "**#{property}** Property"
114-
content += "\n\n(_required_)" if prop_type.required?
115-
content += "\n\n#{prop_type.doc}" unless prop_type.doc.nil?
114+
content += "\n\n(_required_)" if prop_type[:required?]
115+
content += "\n\n#{prop_type[:doc]}" unless prop_type[:doc].nil?
116116
content
117117
end
118118

@@ -124,7 +124,7 @@ def self.get_call_named_function_expression_content(item)
124124

125125
# TODO: what about rvalue?
126126
content = "**#{func_name}** Function\n\n" # TODO: Do I add in the params from the arity number?
127-
content += func_info[:doc]
127+
content += func_info.doc
128128

129129
content
130130
end

0 commit comments

Comments
 (0)